Files
wagic/projects/mtg/src/AllAbilities.cpp
2016-07-27 07:11:30 +08:00

7986 lines
238 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "PrecompiledHeader.h"
#include "AllAbilities.h"
#include "Translate.h"
//display a text animation, this is not a real ability.
MTGEventText::MTGEventText(GameObserver* observer, int _id, MTGCardInstance * card, string textToShow) :
MTGAbility(observer, _id,card)
{
textAlpha = 255;
text = textToShow;
}
void MTGEventText::Update(float dt)
{
if (textAlpha)
{
textAlpha -= static_cast<int> (200 * dt);
Render();
if (textAlpha < 0)
{
textAlpha = 0;
this->forceDestroy = 1;
}
}
MTGAbility::Update(dt);
}
void MTGEventText::Render()
{
if (!textAlpha)
return;
WFont * mFont = WResourceManager::Instance()->GetWFont(Fonts::OPTION_FONT);
float backup = mFont->GetScale();
mFont->SetScale(2 - (float) textAlpha / 130);
mFont->SetColor(ARGB(255,255,255,255));
mFont->DrawString(text.c_str(), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, JGETEXT_CENTER);
mFont->SetScale(backup);
}
MTGEventText * MTGEventText::clone() const
{
return NEW MTGEventText(*this);
}
//generic activated ability for wrapping reveals.
GenericRevealAbility::GenericRevealAbility(GameObserver* observer, int id, MTGCardInstance * source,
Targetable * target, string _howMany) :
ActivatedAbility(observer, id, source, NULL), howMany(_howMany)
{
this->GetId();
}
int GenericRevealAbility::resolve()
{
MTGAbility * ability = NEW MTGRevealingCards(game, this->GetId(), source, howMany);
ability->addToGame();
return 1;
}
const string GenericRevealAbility::getMenuText()
{
return "Reveal Cards";
}
GenericRevealAbility * GenericRevealAbility::clone() const
{
GenericRevealAbility * a = NEW GenericRevealAbility(*this);
return a;
}
GenericRevealAbility::~GenericRevealAbility()
{
//SAFE_DELETE(ability);
}
//carddisplay created for use in abilities.
RevealDisplay::RevealDisplay(int id, GameObserver* game, int x, int y, JGuiListener * listener, TargetChooser * tc,
int nb_displayed_items) :
CardDisplay(id, game, x, y, listener, tc, nb_displayed_items)
{
}
void RevealDisplay::AddCard(MTGCardInstance * _card)
{
CardGui * card = NEW CardView(CardView::nullZone, _card, static_cast<float> (x + 20 + (mObjects.size() - start_item) * 30),
static_cast<float> (y + 25));
Add(card);
}
bool RevealDisplay::CheckUserInput(JButton key)
{
if (JGE_BTN_SEC == key || JGE_BTN_PRI == key || JGE_BTN_UP == key || JGE_BTN_DOWN == key)
return false;
return CardDisplay::CheckUserInput(key);
}
//display card selector box of specified zone.
MTGRevealingCards::MTGRevealingCards(GameObserver* observer, int _id, MTGCardInstance * card, string coreAbility) :
MTGAbility(observer, _id, card), CardDisplay(_id, game, x, y, listener, NULL, nb_displayed_items)
{
abilityToCast = NULL;
revealDisplay = NULL;
abilityFirst = NULL;
abilitySecond = NULL;
abilityString = coreAbility;
initCD = false;
afterReveal = "";
afterEffectActivated = false;
repeat = false;
playerForZone = NULL;
revealCertainTypes = "";
revealUntil = "";
if (card->playerTarget)
playerForZone = card->playerTarget;
else
playerForZone = source->controller();
RevealZone = playerForZone->game->reveal;
zone = RevealZone;
RevealFromZone = playerForZone->game->library;
vector<string>amount = parseBetween(coreAbility, "", " ");
if (amount.size())
{
number = amount[1];
}
vector<string>differentZone = parseBetween(coreAbility, "revealzone(", ")");
if (differentZone.size())
{
RevealFromZone = MTGGameZone::stringToZone(game,differentZone[1],source,NULL);
}
vector<string>certainTypes = parseBetween(coreAbility, "revealtype(", ")");
if (certainTypes.size())
{
revealCertainTypes = certainTypes[1];
}
vector<string>RevealCardUntil = parseBetween(coreAbility, "revealuntil(", ")");
if (RevealCardUntil.size())
{
revealUntil = RevealCardUntil[1];
}
vector<string>first = parseBetween(coreAbility, "optionone ", " optiononeend");
if (first.size())
{
abilityOne = first[1];
}
vector<string>second = parseBetween(coreAbility, "optiontwo ", " optiontwoend");
if (second.size())
{
abilityTwo = second[1];
}
vector<string>afterEffect = parseBetween(coreAbility, "afterrevealed ", " afterrevealedend");
if (afterEffect.size())
{
afterReveal = afterEffect[1];
}
repeat = coreAbility.find("repeat") != string::npos;
}
void MTGRevealingCards::Update(float dt)
{
if (game->OpenedDisplay != this->revealDisplay && !initCD)//wait your turn
{
//if any carddisplays are open, dont do anything until theyre closed, then wait your turn if multiple reveals trigger.
return;
}
if (game->mLayers->actionLayer()->menuObject)
return;//dont do any of this if a menuobject exist.
if (!source->getObserver()->mLayers->actionLayer()->getCurrentTargetChooser() && !revealDisplay && !initCD)
{
WParsedInt nbCardP(number, NULL, source);
nbCard = nbCardP.getValue();
int adjust = 0;
switch (nbCard)
{
//adjust length and location of carddisplay box.
case 1:adjust = 120; break;
case 2:adjust = 145; break;
case 3:adjust = 175; break;
case 4:adjust = 200; break;
case 5:adjust = 225; break;
default:adjust = 225; break;
}
if (revealUntil.size())
{
adjust = 225;
revealDisplay = NEW RevealDisplay(1, game, SCREEN_WIDTH - adjust, SCREEN_HEIGHT, listener, NULL,5);
}
else
revealDisplay = NEW RevealDisplay(1, game, SCREEN_WIDTH - adjust, SCREEN_HEIGHT, listener, NULL, nbCard > 5 ? 5 : nbCard);
revealDisplay->zone = RevealFromZone;
trashDisplays.push_back(revealDisplay);
if (revealCertainTypes.size())//revealing cards of a TARGETCHOOSER type.
{
TargetChooserFactory tcf(game);
TargetChooser * rTc = tcf.createTargetChooser(revealCertainTypes, source);
int startingNumber = RevealFromZone->nb_cards - 1;
if (rTc)
for (int i = startingNumber; i > -1; i--)
{
if (!RevealFromZone->cards.size())
break;
MTGCardInstance * toMove = RevealFromZone->cards[i];
if (toMove)
{
if (rTc->canTarget(toMove, true))
{
CardViewBackup(toMove);
playerForZone->game->putInZone(toMove, RevealFromZone, RevealZone);
source->revealedLast = toMove;
}
}
}
SAFE_DELETE(rTc);
}
else if(revealUntil.size())//reveal cards until you reveal a TARGETCHOOSER.
{
TargetChooserFactory tcf(game);
TargetChooser * rUc = tcf.createTargetChooser(revealUntil, source);
bool foundCard = false;
int howMany = nbCard;
int startingNumber = RevealFromZone->nb_cards;
for (int i = 0; i < startingNumber; i++)
{
if (foundCard && howMany == 0)
break;
if (howMany == 0)
break; //not allowed to reveal until 0 of something is revealed.
if (RevealFromZone->nb_cards - 1 < 0)
break;
MTGCardInstance * toMove = RevealFromZone->cards[RevealFromZone->nb_cards - 1];
if (toMove)
{
if (rUc->canTarget(toMove, true))
{
foundCard = true;
howMany--;
}
CardViewBackup(toMove);
playerForZone->game->putInZone(toMove, RevealFromZone, RevealZone);
source->revealedLast = toMove;
}
}
SAFE_DELETE(rUc);
}
else
{
for (int i = 0; i < nbCard; i++)//normal reveal
{
if (RevealFromZone->nb_cards - 1 < 0)
break;
MTGCardInstance * toMove = RevealFromZone->cards[RevealFromZone->nb_cards - 1];
if (toMove)
{
CardViewBackup(toMove);
playerForZone->game->putInZone(toMove, RevealFromZone, RevealZone);
source->revealedLast = toMove;
}
}
}
//build the zone, create the first ability.
revealDisplay->init(RevealZone);
revealDisplay->zone = RevealZone;
game->OpenedDisplay = revealDisplay;
toResolve();
initCD = true;
}
//card display is ready and loaded, abilities have fired at this point.
//critical for testdestroy, a function that determines if a ability can
//exist in condiations such as source not being in play.
if (!zone->cards.size())
{
//all possible actions are done, the zone is empty, lets NULL it so it clears it off the screen.
//DO NOT SAFE_DELETE here, it destroys the card->view and backups kept for the second ability.
revealDisplay = NULL;
game->OpenedDisplay = revealDisplay;
if (repeat)
{
initCD = false;
}
else if (afterReveal.size() && !afterEffectActivated)
{
afterEffectActivated = true;
abilityAfter = contructAbility(afterReveal);
game->addObserver(abilityAfter);
}
else
this->removeFromGame();
}
if (revealDisplay)
{
revealDisplay->Update(dt);
Render();
}
MTGAbility::Update(dt);
}
void MTGRevealingCards::CardViewBackup(MTGCardInstance * backup)
{
CardView* t;
t = NEW CardView(CardView::nullZone, backup, 0, 0);
//we store copies of the card view since the safe_delete of card displays also deletes the guis stored in them.
t->actX = SCREEN_WIDTH;
t->actY = SCREEN_HEIGHT * -2;
//correct cards x and y, last known location was the reveal display.
cards.push_back(t);
return;
}
int MTGRevealingCards::testDestroy()
{
if (game->mExtraPayment)
return 0;
if (revealDisplay)
return 0;
if (zone->cards.size())
return 0;
if (!initCD)
return 0;
if (game->mLayers->actionLayer()->menuObject)
return 0;
if (game->mLayers->actionLayer()->getIndexOf(abilityFirst) != -1)
return 0;
return 1;
}
int MTGRevealingCards::toResolve()
{
TargetChooserFactory tcf(game);
vector<string>splitTarget = parseBetween(abilityOne, "target(", ")");
//we build a tc to check if the first ability has any valid targets, if it doesnt, just add the 2nd one.
if (splitTarget.size())
{
TargetChooser * rTc = tcf.createTargetChooser(splitTarget[1].c_str(), source);
if (rTc && rTc->countValidTargets())
{
abilityFirst = contructAbility(abilityOne);
game->addObserver(abilityFirst);
}
else
{
repeat = false;
abilitySecond = contructAbility(abilityTwo);
game->addObserver(abilitySecond);
}
SAFE_DELETE(rTc);
}
else//the first ability is not targeted
{
abilityFirst = contructAbility(abilityOne);
game->addObserver(abilityFirst);
}
return 1;
}
MTGAbility * MTGRevealingCards::contructAbility(string abilityToMake)
{
AbilityFactory af(game);
abilityToCast = af.parseMagicLine(abilityToMake, getMaxId(), NULL, source, false);
if (!abilityToCast)
return NULL;
abilityToCast->canBeInterrupted = false;
abilityToCast->forceDestroy = 1;
return abilityToCast;
}
void MTGRevealingCards::Render()
{
if (!revealDisplay)
return;
CheckUserInput(mEngine->ReadButton());
revealDisplay->CheckUserInput(mEngine->ReadButton());
revealDisplay->Render();
return;
}
bool MTGRevealingCards::CheckUserInput(JButton key)
{
//DO NOT REFACTOR BELOW, IT KEPT SPLIT UP TO MAINTAIN READABILITY.
//we override check inputs, we MUST complete reveal and its effects before being allowed to do anything else.
TargetChooser * tc = this->observer->mLayers->actionLayer()->getCurrentTargetChooser();
if (this->source->controller()->isAI())
{
if (this->source->controller() != game->isInterrupting)
game->mLayers->stackLayer()->cancelInterruptOffer(ActionStack::DONT_INTERRUPT, false);
}
if (JGE_BTN_SEC == key || JGE_BTN_PREV == key || JGE_BTN_NEXT == key || JGE_BTN_MENU == key)//android back button
{
if (tc && (tc->targetMin == false || tc->maxtargets == TargetChooser::UNLITMITED_TARGETS))
{
tc->done = true;
tc->forceTargetListReadyByPlayer = 1;
//this is for when we have <upto:x> targets but only want to move Y targets, it allows us to
//tell the targetchooser we are done.
if (!abilitySecond && !tc->getNbTargets() && tc->source)
{//we selected nothing for the first ability.
tc->source->getObserver()->cardClick(tc->source, 0, false);
if (abilityFirst)///some abilities resolve themselves and remove faster than you can removethem from the game.
{
abilityFirst->removeFromGame();
game->mLayers->stackLayer()->Remove(abilityFirst);
}
game->Update(0);
//remove it from the game, update, and remove it from stack if needed.
//before adding next ability, otherwise we end up with a menu reactToClick.
if (zone->cards.size() && abilityFirst->testDestroy())//generally only want to add ability 2 if anything is left in the zone.
{
repeat = false;
abilitySecond = contructAbility(abilityTwo);
game->addObserver(abilitySecond);
}
}
else if (tc->source)
{
tc->source->getObserver()->cardClick(tc->source, 0, false);
}
}
else if (!tc && !abilitySecond)//the actions of the first card have finished and we're done looking at the cards.
{ //or the first ability was an "all(" which was not a mover ability.
CheckUserInput(JGE_BTN_OK);
}
return false;
}
if (JGE_BTN_OK == key)//for ease if we're sitting there looking at the card display and click a card after first ability.
{ //looks redundent and can be added above as another condiational, however we would end up with a massive
//if statement that becomes very very hard to follow.
if (!tc && !abilitySecond)
{
if (abilityFirst)
{
abilityFirst->removeFromGame();
game->mLayers->stackLayer()->Remove(abilityFirst);
}
game->Update(1);
if (zone->cards.size())
{
repeat = false;
abilitySecond = contructAbility(abilityTwo);
game->addObserver(abilitySecond);
}
}
}
if(revealDisplay)
return revealDisplay->CheckUserInput(key);
return false;
}
MTGRevealingCards * MTGRevealingCards::clone() const
{
return NEW MTGRevealingCards(*this);
}
MTGRevealingCards::~MTGRevealingCards()
{
for (vector<CardDisplay*>::iterator it = trashDisplays.begin(); it != trashDisplays.end(); ++it)
SAFE_DELETE(*it);
for (vector<CardView*>::iterator it = cards.begin(); it != cards.end(); ++it)
SAFE_DELETE(*it);
}
int MTGRevealingCards::receiveEvent(WEvent* e)
{
if (WEventZoneChange* event = dynamic_cast<WEventZoneChange*>(e))
{
if (event->from == zone)
{
CardView* t;
if (event->card->view)
t = NEW CardView(CardView::nullZone, event->card, *(event->card->view));
else
t = NEW CardView(CardView::nullZone, event->card, (float)x, (float)y);
//we store copies of the card view since moving to and from card displays also deletes the guis stored in cards.
//GuiLayer::resetObjects() is the main reason we need to back them up. card views are set to NULL maybe more often than
//they should be, possibly someone being to over cautious.
t->actX = SCREEN_WIDTH;
t->actY = SCREEN_HEIGHT * -2;
//correct cards x and y, last known location was the reveal display.
cards.push_back(t);
return 1;
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
////scry//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//below the effect of "scry X, THEN reveal and do stuff, was impossible to accomplish with reveal alone.
//if a card simply states "scry X" and nothing else use reveal:x.
//this is for effects that want you to reveal AFTER you scry.
//this ability automatically creates effects, put on top, then whatever you dont get put on buttom,
//then it reveals the top card, and creates the ability written in primitive as core, then it
//handles putting the card back on top of the library when you are done.
///delayed changes the order, makes the ability fire after the 2nd reveal is finished.
///
MTGScryCards::MTGScryCards(GameObserver* observer, int _id, MTGCardInstance * card, string coreAbility) :
MTGAbility(observer, _id, card), CardDisplay(_id, game, x, y, listener, NULL, nb_displayed_items)
{
abilityToCast = NULL;
revealDisplay = NULL;
abilityFirst = NULL;
abilitySecond = NULL;
abilityString = coreAbility;
delayedAbilityString = "";
revealTopAmount = 1;//scry, then reveal the top card and do effect.
initCD = false;
RevealZone = source->controller()->game->reveal;
zone =RevealZone;
RevealFromZone = source->controller()->game->library;
vector<string>amount = parseBetween(coreAbility, "", " ");
if (amount.size())
{
number = amount[1];
}
vector<string>differentZone = parseBetween(coreAbility, "scryzone(", ")");
if (differentZone.size())
{
RevealFromZone = MTGGameZone::stringToZone(game, differentZone[1], source, NULL);
}
abilityOne = "name(Place on top) target(<anyamount>*|myreveal) moveto(mylibrary)";
delayed = coreAbility.find("delayed") != string::npos;
dontRevealAfter = coreAbility.find("dontshow") != string::npos;
if(dontRevealAfter)
revealTopAmount = 0;
vector<string>second = parseBetween(coreAbility, "scrycore ", " scrycoreend");
if (second.size())
{
if (delayed)
{
abilityTwo = "target(*|reveal) name(Reveal the top card) donothing";
delayedAbilityString = second[1];
}
else
abilityTwo = second[1];
}
}
void MTGScryCards::Update(float dt)
{
if (game->OpenedDisplay != this->revealDisplay && !initCD)
return;
if (game->mLayers->actionLayer()->menuObject)
return;
if (!source->getObserver()->mLayers->actionLayer()->getCurrentTargetChooser() && !revealDisplay && !initCD)
{
WParsedInt nbCardP(number, NULL, source);
nbCard = nbCardP.getValue();
initDisplay(nbCard);
toResolve();
}
initCD = true;
if (!zone->cards.size() && abilitySecond)
{
revealDisplay = NULL;
game->OpenedDisplay = revealDisplay;
this->removeFromGame();
}
if (revealDisplay)
{
revealDisplay->Update(dt);
Render();
}
MTGAbility::Update(dt);
}
void MTGScryCards::initDisplay(int value)
{
if (RevealZone->cards.size())
{
do
{
MTGCardInstance * toMove = RevealZone->cards[0];
if (toMove)
{
MTGAbility * a = NEW AALibraryBottom(game, getMaxId(), source, toMove);
a->oneShot = 1;
a->resolve();
SAFE_DELETE(a);
}
} while (RevealZone->cards.size());
game->Update(0);
revealDisplay = NULL;
game->OpenedDisplay = revealDisplay;
}
int adjust = 0;
switch (value)
{
case 1:adjust = 120; break;
case 2:adjust = 145; break;
case 3:adjust = 175; break;
case 4:adjust = 200; break;
case 5:adjust = 225; break;
default:adjust = 225; break;
}
revealDisplay = NEW RevealDisplay(1, game, SCREEN_WIDTH - adjust, SCREEN_HEIGHT, listener, NULL, nbCard > 5 ? 5 : nbCard);
revealDisplay->zone = RevealFromZone;
trashDisplays.push_back(revealDisplay);
for (int i = 0; i < value; i++)
{
if (RevealFromZone->nb_cards - 1 < 0)
break;
MTGCardInstance * toMove = RevealFromZone->cards[RevealFromZone->nb_cards - 1];
if (toMove)
{
CardView* t;
t = NEW CardView(CardView::nullZone, toMove, 0, 0);
t->actX = SCREEN_WIDTH;
t->actY = SCREEN_HEIGHT * -2;
cards.push_back(t);
source->controller()->game->putInZone(toMove, RevealFromZone, RevealZone);
source->revealedLast = toMove;
}
}
revealDisplay->init(RevealZone);
revealDisplay->zone = RevealZone;
game->OpenedDisplay = revealDisplay;
}
int MTGScryCards::testDestroy()
{
if (game->mExtraPayment)
return 0;
if (revealDisplay)
return 0;
if (zone->cards.size())
return 0;
if (!initCD)
return 0;
if (game->mLayers->actionLayer()->menuObject)
return 0;
if (game->mLayers->actionLayer()->getIndexOf(abilityFirst) != -1)
return 0;
return 1;
}
int MTGScryCards::toResolve()
{
//scry will always have valid targets.
abilityFirst = contructAbility(abilityOne);
game->addObserver(abilityFirst);
return 1;
}
MTGAbility * MTGScryCards::contructAbility(string abilityToMake)
{
AbilityFactory af(game);
abilityToCast = af.parseMagicLine(abilityToMake, getMaxId(), NULL, source, false);
if (!abilityToCast)
return NULL;
abilityToCast->canBeInterrupted = false;
abilityToCast->forceDestroy = 1;
return abilityToCast;
}
void MTGScryCards::Render()
{
if (!revealDisplay)
return;
CheckUserInput(mEngine->ReadButton());
if (revealDisplay)
{
revealDisplay->CheckUserInput(mEngine->ReadButton());
revealDisplay->Render();
}
return;
}
bool MTGScryCards::CheckUserInput(JButton key)
{
//DO NOT REFACTOR BELOW
TargetChooser * tc = this->observer->mLayers->actionLayer()->getCurrentTargetChooser();
if (this->source->controller()->isAI())
{//ai doesnt click button, and the engine has no way of knowing whos clicking button
//for now we will cancel interrupts made when ai is making choice
//in the future we will need a way to find out if the human is pressing the keys and which player.
if (this->source->controller() != game->isInterrupting)
game->mLayers->stackLayer()->cancelInterruptOffer(ActionStack::DONT_INTERRUPT, false);
}
if (JGE_BTN_SEC == key || JGE_BTN_PREV == key || JGE_BTN_NEXT == key || JGE_BTN_MENU == key)
{
if (tc && (tc->targetMin == false || tc->maxtargets == TargetChooser::UNLITMITED_TARGETS))
{
tc->done = true;
tc->forceTargetListReadyByPlayer = 1;
if (!abilitySecond && !tc->getNbTargets() && tc->source)
{
tc->source->getObserver()->cardClick(tc->source, 0, false);
if (abilityFirst)///some abilities resolve themselves and remove faster than you can removethem from the game.
{
abilityFirst->removeFromGame();
game->mLayers->stackLayer()->Remove(abilityFirst);
}
game->Update(0);
if (zone->cards.size() && abilityFirst->testDestroy())
{
initDisplay(revealTopAmount);
abilitySecond = contructAbility(abilityTwo);
game->addObserver(abilitySecond);
}
}
else if (tc->source)
{
tc->source->getObserver()->cardClick(tc->source, 0, false);
}
}
else if (!tc && !abilitySecond)
{
CheckUserInput(JGE_BTN_OK);
}
return false;
}
if (JGE_BTN_OK == key)
{
if (!tc && !abilitySecond)
{
if (abilityFirst)
{
abilityFirst->removeFromGame();
game->mLayers->stackLayer()->Remove(abilityFirst);
}
game->Update(1);
if (zone->cards.size() || (revealDisplay && !zone->cards.size()))
{
initDisplay(revealTopAmount);
abilitySecond = contructAbility(abilityTwo);
game->addObserver(abilitySecond);
}
}
if (!tc && abilitySecond && abilitySecond->testDestroy())
{
do
{
if (!RevealZone->cards.size())
break;
MTGCardInstance * toMove = RevealZone->cards[0];
if (toMove)
{
source->revealedLast = toMove;
MTGAbility * a = NEW AAMover(game, getMaxId(), source, toMove,"library", "Place on top");
a->oneShot = true;
a->resolve();
SAFE_DELETE(a);
}
} while (RevealZone->cards.size());
if (delayed)
{
MTGAbility * delayedA = contructAbility(delayedAbilityString);
if (delayedA->oneShot)
{
delayedA->resolve();
SAFE_DELETE(delayedA);
}
else
delayedA->addToGame();
}
}
}
if (revealDisplay)
return revealDisplay->CheckUserInput(key);
return false;
}
MTGScryCards * MTGScryCards::clone() const
{
return NEW MTGScryCards(*this);
}
MTGScryCards::~MTGScryCards()
{
for (vector<CardDisplay*>::iterator it = trashDisplays.begin(); it != trashDisplays.end(); ++it)
SAFE_DELETE(*it);
for (vector<CardView*>::iterator it = cards.begin(); it != cards.end(); ++it)
SAFE_DELETE(*it);
}
int MTGScryCards::receiveEvent(WEvent* e)
{
if (WEventZoneChange* event = dynamic_cast<WEventZoneChange*>(e))
{
if (event->from == zone)
{
CardView* t;
if (event->card->view)
t = NEW CardView(CardView::nullZone, event->card, *(event->card->view));
else
t = NEW CardView(CardView::nullZone, event->card, (float)x, (float)y);
//we store copies of the card view since moving to and from card displays also deletes the guis stored in cards.
//GuiLayer::resetObjects() is the main reason we need to back them up. card views are set to NULL maybe more often than
//they should be, possibly someone being to over cautious.
t->actX = SCREEN_WIDTH;
t->actY = SCREEN_HEIGHT * -2;
//correct cards x and y, last known location was the reveal display.
cards.push_back(t);
return 1;
}
}
return 0;
}
//scry wrapper
GenericScryAbility::GenericScryAbility(GameObserver* observer, int id, MTGCardInstance * source,
Targetable * target, string _howMany) :
ActivatedAbility(observer, id, source, NULL), howMany(_howMany)
{
this->GetId();
}
int GenericScryAbility::resolve()
{
MTGAbility * ability = NEW MTGScryCards(game, this->GetId(), source, howMany);
ability->addToGame();
return 1;
}
const string GenericScryAbility::getMenuText()
{
return "Scry Cards";
}
GenericScryAbility * GenericScryAbility::clone() const
{
GenericScryAbility * a = NEW GenericScryAbility(*this);
return a;
}
GenericScryAbility::~GenericScryAbility()
{
//SAFE_DELETE(ability);
}
////////////////////////
//Activated Abilities
//Generic Activated Abilities
GenericActivatedAbility::GenericActivatedAbility(GameObserver* observer, string newName, string castRestriction, int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost,
string limit,MTGAbility * sideEffects,string usesBeforeSideEffects, int restrictions, MTGGameZone * dest) :
ActivatedAbility(observer, _id, card, _cost, restrictions,limit,sideEffects,usesBeforeSideEffects,castRestriction), NestedAbility(a), activeZone(dest),newName(newName)
{
counters = 0;
target = ability->target;
}
GenericActivatedAbility::GenericActivatedAbility(const GenericActivatedAbility &other):
ActivatedAbility(other), NestedAbility(other), activeZone(other.activeZone), newName(other.newName)
{
}
int GenericActivatedAbility::resolve()
{
//Note: I've seen a similar block in some other MTGAbility, can this be refactored .
if (abilityCost)
{
source->X = 0;
ManaCost * diff = abilityCost->Diff(getCost());
source->X = diff->hasX();
SAFE_DELETE(diff);
}
ability->target = target; //may have been updated...
if (ability)
return ability->resolve();
return 0;
}
const string GenericActivatedAbility::getMenuText()
{
if(newName.size())
return newName.c_str();
if (ability)
return ability->getMenuText();
return "Error";
}
int GenericActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
{
if (dynamic_cast<AAMorph*> (ability) && !card->isMorphed && !card->morphed && card->turningOver)
return 0;
return ActivatedAbility::isReactingToClick(card, mana);
}
void GenericActivatedAbility::Update(float dt)
{
ActivatedAbility::Update(dt);
}
int GenericActivatedAbility::testDestroy()
{
if (!activeZone)
return ActivatedAbility::testDestroy();
if (activeZone->hasCard(source))
return 0;
return 1;
}
GenericActivatedAbility * GenericActivatedAbility::clone() const
{
GenericActivatedAbility * a = NEW GenericActivatedAbility(*this);
a->ability = ability->clone();
return a;
}
GenericActivatedAbility::~GenericActivatedAbility()
{
SAFE_DELETE(ability);
}
//AA Alter Poison
AAAlterPoison::AAAlterPoison(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, int poison, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who), poison(poison)
{
}
int AAAlterPoison::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
Player * pTarget = (Player*)_target;
if(!pTarget->inPlay()->hasAbility(Constants::POISONSHROUD) || poison < 0)
_target->poisonCount += poison;
}
return 0;
}
const string AAAlterPoison::getMenuText()
{
return "Poison";
}
AAAlterPoison * AAAlterPoison::clone() const
{
return NEW AAAlterPoison(*this);
}
AAAlterPoison::~AAAlterPoison()
{
}
//Damage Prevent
AADamagePrevent::AADamagePrevent(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, int preventing, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who), preventing(preventing)
{
aType = MTGAbility::STANDARD_PREVENT;
}
int AADamagePrevent::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
_target->preventable += preventing;
}
return 0;
}
const string AADamagePrevent::getMenuText()
{
return "Prevent Damage";
}
AADamagePrevent * AADamagePrevent::clone() const
{
return NEW AADamagePrevent(*this);
}
AADamagePrevent::~AADamagePrevent()
{
}
//AADamager
AADamager::AADamager(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, string d, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who), d(d)
{
aType = MTGAbility::DAMAGER;
redirected = false;
}
int AADamager::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
WParsedInt damage(d, NULL, (MTGCardInstance *)source);
if(_target == game->opponent() && game->opponent()->inPlay()->hasType("planeswalker") && !redirected)
{
vector<MTGAbility*>selection;
MTGCardInstance * check = NULL;
this->redirected = true;
MTGAbility * setPlayer = this->clone();
this->redirected = false;
selection.push_back(setPlayer);
int checkWalkers = ((Player*)_target)->game->battlefield->cards.size();
for(int i = 0; i < checkWalkers;++i)
{
check = ((Player*)_target)->game->battlefield->cards[i];
if(check->hasType(Subtypes::TYPE_PLANESWALKER))
{
this->redirected = true;
MTGAbility * setWalker = this->clone();
this->redirected = false;
setWalker->oneShot = true;
setWalker->target = check;
selection.push_back(setWalker);
}
}
if(selection.size())
{
MTGAbility * a1 = NEW MenuAbility(game, this->GetId(), source, source,true,selection);
game->mLayers->actionLayer()->currentActionCard = source;
a1->resolve();
}
return 1;
}
game->mLayers->stackLayer()->addDamage(source, _target, damage.getValue());
game->mLayers->stackLayer()->resolve();
return 1;
}
return 0;
}
int AADamager::getDamage()
{
WParsedInt damage(d, NULL, (MTGCardInstance *)source);
return damage.getValue();
}
const string AADamager::getMenuText()
{
MTGCardInstance * _target = dynamic_cast<MTGCardInstance*>(target);
if(_target && _target->hasType(Subtypes::TYPE_PLANESWALKER))
return _target->name.c_str();
if(redirected)
return "Damage Player";
return "Damage";
}
AADamager * AADamager::clone() const
{
return NEW AADamager(*this);
}
//AADepleter
AADepleter::AADepleter(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost, int who, bool toexile, bool colorrepeat, bool namerepeat) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who),nbcardsStr(nbcardsStr),toexile(toexile), colorrepeat(colorrepeat), namerepeat(namerepeat)
{
}
int AADepleter::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
WParsedInt numCards(nbcardsStr, NULL, source);
MTGLibrary * library = player->game->library;
if (colorrepeat && library->nb_cards)
{
bool repeating = false;
do
{
repeating = false;
vector<MTGCardInstance*>found;
for (int i = 0; i < numCards.getValue(); i++)
{
if (library->nb_cards)
{
if(library->nb_cards > i)
found.push_back(library->cards[(library->nb_cards - 1) - i]);
}
}
for (vector<MTGCardInstance*>::iterator it = found.begin(); it != found.end(); it++)
{
MTGCardInstance * cardFirst = *it;
if (cardFirst->isLand())
continue;
for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i)
{
if (cardFirst->hasColor(i))
{
for (vector<MTGCardInstance*>::iterator secondit = found.begin(); secondit != found.end(); secondit++)
{
MTGCardInstance * cardSecond = *secondit;
if (cardSecond->isLand())
continue;
if (cardSecond->hasColor(i) && cardFirst != cardSecond)
{
repeating = true;
}
}
}
}
}
do
{
if (found.size())
{
MTGCardInstance * toMove = found.back();
if (toMove)
{
if (toexile)
player->game->putInZone(toMove, library, player->game->exile);
else
player->game->putInZone(toMove, library, player->game->graveyard);
found.pop_back();
}
}
} while (found.size());
} while (repeating);
}
else if (namerepeat && library->nb_cards)
{
bool repeating = false;
do
{
repeating = false;
vector<MTGCardInstance*>found;
for (int i = 0; i < numCards.getValue(); i++)
{
if (library->nb_cards)
{
if (library->nb_cards > i)
found.push_back(library->cards[(library->nb_cards - 1) - i]);
}
}
for (vector<MTGCardInstance*>::iterator it = found.begin(); it != found.end(); it++)
{
MTGCardInstance * cardFirst = *it;
for (vector<MTGCardInstance*>::iterator secondit = found.begin(); secondit != found.end(); secondit++)
{
MTGCardInstance * cardSecond = *secondit;
if (cardSecond->name == cardFirst->name && cardFirst != cardSecond)
{
repeating = true;
}
}
}
do
{
if (found.size())
{
MTGCardInstance * toMove = found.back();
if (toMove)
{
if (toexile)
player->game->putInZone(toMove, library, player->game->exile);
else
player->game->putInZone(toMove, library, player->game->graveyard);
found.pop_back();
}
}
} while (found.size());
} while (repeating);
}
else
{
for (int i = 0; i < numCards.getValue(); i++)
{
if (library->nb_cards)
{
if (toexile)
player->game->putInZone(library->cards[library->nb_cards - 1], library, player->game->exile);
else
player->game->putInZone(library->cards[library->nb_cards - 1], library, player->game->graveyard);
}
}
}
}
return 1;
}
const string AADepleter::getMenuText()
{
if(toexile)
return "Ingest";
return "Deplete";
}
AADepleter * AADepleter::clone() const
{
return NEW AADepleter(*this);
}
//AACascade
AACascade::AACascade(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, string nbcardsStr, ManaCost * _cost) :
ActivatedAbility(observer, _id, _source, _cost, 0),nbcardsStr(nbcardsStr)
{
selectedCards.clear();
oldOrder.clear();
newOrder.clear();
castingThis = NULL;
}
int AACascade::resolve()
{
Player * player = source->controller();
if (!player)
return 0;
WParsedInt numCards(nbcardsStr, NULL, source);
MTGLibrary * library = player->game->library;
MTGRemovedFromGame * exile = player->game->exile;
MTGCardInstance * viable = NULL;
int counter = 0;
bool found = false;
for (int i = 0; i < numCards.getValue(); i++)
{
//*//*//*//
if (found)
continue;
//////////////////////////////////////////////
if (!library->nb_cards)
continue;
//////////////////////////////////////////////
while (library->nb_cards && !found)
{
viable = library->cards[library->nb_cards -1];
if (!found)
{
if (!viable->isLand() && (viable->getManaCost()->getConvertedCost() < source->getManaCost()->getConvertedCost()))
{
viable = player->game->putInZone(viable, library, exile);
viable->isCascaded = true;
castingThis = viable;
found = true;
}
else
{
viable = player->game->putInZone(viable, library, exile);
viable->isCascaded = true;
counter++;
}
}
}
//*//*//*//*
}
////////////////////////////////////////////
for (int j = 0; j < exile->nb_cards; j++)
{
if (exile->cards[j]->isCascaded)
{
MTGCardInstance * CardToPutBack = exile->cards[j];;
CardToPutBack->isCascaded = false;
selectedCards.push_back(CardToPutBack);
}
}
//////////////////////////////////////////
if (selectedCards.size())
{
do
{
MTGCardInstance * toMove = selectedCards.back();
if (toMove)
{
MTGAbility * a = NEW AALibraryBottom(game, game->mLayers->actionLayer()->getMaxId(), source, toMove);
a->oneShot = 1;
a->resolve();
SAFE_DELETE(a);
selectedCards.pop_back();
}
} while (selectedCards.size());
if (castingThis)
{
while (castingThis->next)
castingThis = castingThis->next;
toCastCard(castingThis);
}
}
//////////////////////////////////////
return 1;
}
void AACascade::toCastCard(MTGCardInstance * thisCard)
{
MTGAbility *ac = NEW AACastCard(game, game->mLayers->actionLayer()->getMaxId(), thisCard, thisCard,false,false,true,"","",false,false);
MayAbility *ma1 = NEW MayAbility(game, game->mLayers->actionLayer()->getMaxId(), ac->clone(), thisCard,false);
MTGAbility *ga1 = NEW GenericAddToGame(game, game->mLayers->actionLayer()->getMaxId(), thisCard,NULL,ma1->clone());
SAFE_DELETE(ac);
SAFE_DELETE(ma1);
ga1->resolve();
SAFE_DELETE(ga1);
return;
}
const string AACascade::getMenuText()
{
return "Cascade";
}
AACascade * AACascade::clone() const
{
return NEW AACascade(*this);
}
//take extra turns or skip turns, values in the negitive will make you skip.
AAModTurn::AAModTurn(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbTurnStr, ManaCost * _cost, int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who),nbTurnStr(nbTurnStr)
{
}
int AAModTurn::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
WParsedInt numTurns(nbTurnStr, NULL, source);
if(numTurns.getValue() > 0)
{
player->extraTurn += numTurns.getValue();
}
else
{
player->skippingTurn += abs(numTurns.getValue());
}
}
return 1;
}
const string AAModTurn::getMenuText()
{
WParsedInt numTurns(nbTurnStr, NULL, source);
if(numTurns.getValue() > 0)
return "Take Extra Turn(s)";
else
return "Skip A Turn(s)";
}
AAModTurn * AAModTurn::clone() const
{
return NEW AAModTurn(*this);
}
//move target to bottom of owners library
AALibraryBottom::AALibraryBottom(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, _source, _cost, 0)
{
target = _target;
}
int AALibraryBottom::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
_target = _target->owner->game->putInLibrary(_target);
if (_target)
{
MTGLibrary * library = _target->owner->game->library;
vector<MTGCardInstance *>oldOrder = library->cards;
vector<MTGCardInstance *>newOrder;
newOrder.push_back(_target);
for(unsigned int k = 0;k < oldOrder.size();++k)
{
MTGCardInstance * rearranged = oldOrder[k];
if(rearranged != _target)
newOrder.push_back(rearranged);
}
library->cards = newOrder;
return 1;
}
return 0;
}
const string AALibraryBottom::getMenuText()
{
return "Bottom Of Library";
}
AALibraryBottom * AALibraryBottom::clone() const
{
return NEW AALibraryBottom(*this);
}
//AACopier
AACopier::AACopier(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, _source, _cost, 0)
{
target = _target;
}
int AACopier::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
MTGCard* clone = MTGCollection()->getCardById(_target->copiedID);
MTGCardInstance * myClone = NEW MTGCardInstance(clone, source->controller()->game);
source->copy(myClone);
SAFE_DELETE(myClone);
source->isACopier = true;
source->copiedID = _target->getMTGId();
source->modifiedbAbi = _target->modifiedbAbi;
source->origbasicAbilities = _target->origbasicAbilities;
source->basicAbilities = _target->origbasicAbilities;
if(_target->isMorphed)
{
source->power = 2;
source->life = 2;
source->toughness = 2;
source->setColor(0,1);
source->name = "Morph";
source->types.clear();
string cre = "Creature";
source->setType(cre.c_str());
source->basicAbilities.reset();
source->getManaCost()->resetCosts();
}
return 1;
}
return 0;
}
const string AACopier::getMenuText()
{
return "Copy";
}
AACopier * AACopier::clone() const
{
return NEW AACopier(*this);
}
//phaseout
AAPhaseOut::AAPhaseOut(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, _source, _cost, 0)
{
target = _target;
}
int AAPhaseOut::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
_target->isPhased = true;
_target->phasedTurn = game->turn;
if(_target->view)
_target->view->alpha = 50;
_target->initAttackersDefensers();
return 1;
}
return 0;
}
const string AAPhaseOut::getMenuText()
{
return "Phase Out";
}
AAPhaseOut * AAPhaseOut::clone() const
{
return NEW AAPhaseOut(*this);
}
//AAImprint
AAImprint::AAImprint(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, _source, _cost, 0)
{
target = _target;
}
int AAImprint::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
Player * p = _target->controller();
if(p)
p->game->putInExile(_target);
while(_target->next)
_target = _target->next;
source->imprintedCards.push_back(_target);
if (source->imprintedCards.size())
{
if (source->imprintedCards.back()->hasColor(Constants::MTG_COLOR_GREEN))
source->imprintG += 1;
if (source->imprintedCards.back()->hasColor(Constants::MTG_COLOR_BLUE))
source->imprintU += 1;
if (source->imprintedCards.back()->hasColor(Constants::MTG_COLOR_RED))
source->imprintR += 1;
if (source->imprintedCards.back()->hasColor(Constants::MTG_COLOR_BLACK))
source->imprintB += 1;
if (source->imprintedCards.back()->hasColor(Constants::MTG_COLOR_WHITE))
source->imprintW += 1;
if (source->imprintedCards.back()->getName().size())
{
source->currentimprintName = source->imprintedCards.back()->getName();
source->imprintedNames.push_back(source->imprintedCards.back()->getName());
}
}
return 1;
}
return 0;
}
const string AAImprint::getMenuText()
{
return "Imprint";
}
AAImprint * AAImprint::clone() const
{
return NEW AAImprint(*this);
}
//Counters
AACounter::AACounter(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target,string counterstring, const char * _name, int power, int toughness,
int nb,int maxNb, ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0),counterstring(counterstring), nb(nb),maxNb(maxNb), power(power), toughness(toughness), name(_name)
{
this->target = target;
if (name.find("Level") != string::npos || name.find("level") != string::npos)
aType = MTGAbility::STANDARD_LEVELUP;
else
aType = MTGAbility::COUNTERS;
menu = "";
}
int AACounter::resolve()
{
if (target)
{
MTGCardInstance * _target = (MTGCardInstance *) target;
AbilityFactory af(game);
if(counterstring.size())
{
Counter * checkcounter = af.parseCounter(counterstring, source, NULL);
nb = checkcounter->nb;
delete checkcounter;
}
if (nb > 0)
{
for (int i = 0; i < nb; i++)
{
while (_target->next)
_target = _target->next;
Counter * targetCounter = NULL;
int currentAmount = 0;
if (_target->counters && _target->counters->hasCounter(name.c_str(), power, toughness))
{
targetCounter = _target->counters->hasCounter(name.c_str(), power, toughness);
currentAmount = targetCounter->nb;
}
if(!maxNb || (maxNb && currentAmount < maxNb))
{
_target->counters->addCounter(name.c_str(), power, toughness);
}
}
}
else
{
for (int i = 0; i < -nb; i++)
{
while (_target->next)
_target = _target->next;
_target->counters->removeCounter(name.c_str(), power, toughness);
}
}
_target->doDamageTest = 1;
if(!_target->afterDamage())
{
//If a creature with +1/+1 counters on it gets enough -1/-1 counters to kill it,
//it dies before the two counters have the chance to cancel out. For example,
//if your Strangleroot Geist with a +1/+1 counter on it got three -1/-1 counters
//from Skinrender's "enters the battlefield" ability, the Geist would die with //
//one +1/+1 counter and three -1/-1 counters and wouldn't return to the battlefield.
for (int i = 0; i < _target->counters->mCount; i++)
{
if (_target->counters->counters[i]->cancels(power, toughness) && !name.size() && _target->counters->counters[i]->nb > 0)
{
_target->counters->counters[i]->cancelCounter(power,toughness);
}
}
}
//specail cases, indestructible creatures which recieve enough counters to kill it are destroyed as a state based effect
if(_target->toughness <= 0 && _target->has(Constants::INDESTRUCTIBLE) && toughness < 0)
_target->controller()->game->putInGraveyard(_target);
return nb;
}
return 0;
}
const string AACounter::getMenuText()
{
if (menu.size())
{
return menu.c_str();
}
char buffer[128];
if (name.size())
{
string s = name;
menu.append(s.c_str());
}
if (power != 0 || toughness != 0)
{
sprintf(buffer, " %i/%i", power, toughness);
menu.append(buffer);
}
menu.append(" Counter");
if (nb != 1 && !(nb < -1000))
{
sprintf(buffer, ": %i", nb);
menu.append(buffer);
}
sprintf(menuText, "%s", menu.c_str());
return menuText;
}
AACounter * AACounter::clone() const
{
return NEW AACounter(*this);
}
//shield a card from a certain type of counter.
ACounterShroud::ACounterShroud(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target,TargetChooser * tc, Counter * counter) :
MTGAbility(observer, id, source),csTc(tc),counter(counter),re(NULL)
{
}
int ACounterShroud::addToGame()
{
SAFE_DELETE(re);
re = NEW RECountersPrevention(this,source,(MTGCardInstance*)target,csTc,counter);
if (re)
{
game->replacementEffects->add(re);
return MTGAbility::addToGame();
}
return 0;
}
int ACounterShroud::destroy()
{
game->replacementEffects->remove(re);
SAFE_DELETE(re);
return 1;
}
ACounterShroud * ACounterShroud::clone() const
{
ACounterShroud * a = NEW ACounterShroud(*this);
a->re = NULL;
return a;
}
ACounterShroud::~ACounterShroud()
{
SAFE_DELETE(re);
SAFE_DELETE(counter);
}
//track counters placed on a card
ACounterTracker::ACounterTracker(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, string scounter) :
MTGAbility(observer, id, source, target),scounter(scounter)
{
removed = 0;
}
int ACounterTracker::addToGame()
{
MTGCardInstance * _target = (MTGCardInstance*)target;
AbilityFactory af(game);
Counter * counter = af.parseCounter(scounter, _target, NULL); //(Spell*)source);
if (!counter)
{
return 0;
}
if(_target && !removed)
{
if(_target->counters->hasCounter(counter->name.c_str(),counter->power,counter->toughness) && _target->counters->hasCounter(counter->name.c_str(),counter->power,counter->toughness)->nb >= counter->nb)
{
for(int nb = 0;nb < counter->nb;nb++)
{
_target->counters->removeCounter(counter->name.c_str(),counter->power,counter->toughness);
removed++;
}
}
SAFE_DELETE(counter);
return MTGAbility::addToGame();
}
SAFE_DELETE(counter);
return 0;
}
int ACounterTracker::destroy()
{
MTGCardInstance * _target = (MTGCardInstance*)target;
AbilityFactory af(game);
Counter * counter = af.parseCounter(scounter, _target, NULL); //(Spell*)source);
if (!counter)
{
return 0;
}
if(_target)
{
if(removed == counter->nb)
{
for(int nb = 0;nb < counter->nb;nb++)
{
_target->counters->addCounter(counter->name.c_str(),counter->power,counter->toughness);
}
}
}
SAFE_DELETE(counter);
return 1;
}
int ACounterTracker::testDestroy()
{
if(this->source->isInPlay(game))
return 0;
return 1;
}
ACounterTracker * ACounterTracker::clone() const
{
ACounterTracker * a = NEW ACounterTracker(*this);
return a;
}
ACounterTracker::~ACounterTracker()
{
}
//removeall counters of a certain type or all.
AARemoveAllCounter::AARemoveAllCounter(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, const char * _name, int power, int toughness,
int nb,bool all, ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0), nb(nb), power(power), toughness(toughness), name(_name),all(all)
{
this->target = target;
menu = "";
}
int AARemoveAllCounter::resolve()
{
if (!target)
return 0;
MTGCardInstance * _target = (MTGCardInstance *) target;
if (all )
{
for(int amount = 0;amount < _target->counters->mCount;amount++)
{
while(_target->counters->counters[amount]->nb > 0)
_target->counters->removeCounter(_target->counters->counters[amount]->name.c_str(),_target->counters->counters[amount]->power,_target->counters->counters[amount]->toughness);
}
}
Counter * targetCounter = NULL;
if (_target->counters && _target->counters->hasCounter(name.c_str(), power, toughness))
{
targetCounter = _target->counters->hasCounter(name.c_str(), power, toughness);
nb = targetCounter->nb;
}
for (int i = 0; i < nb; i++)
{
while (_target->next)
_target = _target->next;
_target->counters->removeCounter(name.c_str(), power, toughness);
}
return nb;
}
const string AARemoveAllCounter::getMenuText()
{
if (menu.size())
{
return menu.c_str();
}
char buffer[128];
if (name.size())
{
string s = name;
menu.append(s.c_str());
}
if (power != 0 || toughness != 0)
{
sprintf(buffer, " %i/%i", power, toughness);
menu.append(buffer);
}
menu.append(" Counter Removed");
if (nb != 1)
{
sprintf(buffer, ": %i", nb);
menu.append(buffer);
}
return menu.c_str();
}
AARemoveAllCounter * AARemoveAllCounter::clone() const
{
return NEW AARemoveAllCounter(*this);
}
//proliferate a target
AAProliferate::AAProliferate(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target,ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0)
{
this->GetId();
}
int AAProliferate::resolve()
{
if (!target)
return 0;
vector<MTGAbility*>pcounters;
Player * pTarget = dynamic_cast<Player *>(target);
MTGCardInstance * cTarget = dynamic_cast<MTGCardInstance *>(target);
if(pTarget && pTarget->poisonCount && pTarget != source->controller())
{
MTGAbility * a = NEW AAAlterPoison(game, game->mLayers->actionLayer()->getMaxId(), source, target, 1, NULL);
a->oneShot = true;
pcounters.push_back(a);
}
else if (cTarget && cTarget->counters)
{
Counters * counters = cTarget->counters;
for(size_t i = 0; i < counters->counters.size(); ++i)
{
Counter * counter = counters->counters[i];
MTGAbility * a = NEW AACounter(game, game->mLayers->actionLayer()->getMaxId(), source, cTarget,"", counter->name.c_str(), counter->power, counter->toughness, 1,0);
a->oneShot = true;
pcounters.push_back(a);
}
}
if(pcounters.size())
{
MTGAbility * a = NEW MenuAbility(game, this->GetId(), target, source,false,pcounters);
a->resolve();
}
return 1;
}
const string AAProliferate::getMenuText()
{
return "Proliferate";
}
AAProliferate * AAProliferate::clone() const
{
return NEW AAProliferate(*this);
}
AAProliferate::~AAProliferate()
{
}
//
//choosing a type or color
GenericChooseTypeColor::GenericChooseTypeColor(GameObserver* observer, int id, MTGCardInstance * source, Targetable *,string _toAdd,bool chooseColor,bool nonwall, ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0), baseAbility(_toAdd),chooseColor(chooseColor),ANonWall(nonwall)
{
this->GetId();
setColor = NULL;
}
int GenericChooseTypeColor::resolve()
{
if (!target)
return 0;
vector<MTGAbility*>selection;
if(chooseColor)
{
for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i)
{
setColor = NEW AASetColorChosen(game, game->mLayers->actionLayer()->getMaxId(), source,(MTGCardInstance*)target, i, baseAbility);
MTGAbility * set = setColor->clone();
set->oneShot = true;
selection.push_back(set);
SAFE_DELETE(setColor);
}
}
else
{
vector<string> values = MTGAllCards::getCreatureValuesById();
for (size_t i = 0; i < values.size(); ++i)
{
string menu = values[i];
if (!ANonWall || (menu != "wall" && menu != "Wall"))
{
setType = NEW AASetTypeChosen(game, game->mLayers->actionLayer()->getMaxId(), source,(MTGCardInstance*)target, i,menu,baseAbility);
MTGAbility * set = setType->clone();
set->oneShot = true;
selection.push_back(set);
SAFE_DELETE(setType);
}
}
}
if(selection.size())
{
MTGAbility * a1 = NEW MenuAbility(game, this->GetId(), target, source,true,selection);
game->mLayers->actionLayer()->currentActionCard = (MTGCardInstance *)target;
a1->resolve();
}
return 1;
}
const string GenericChooseTypeColor::getMenuText()
{
if(chooseColor)
return "Choose a color";
else
return "Choose a type";
}
GenericChooseTypeColor * GenericChooseTypeColor::clone() const
{
GenericChooseTypeColor * a = NEW GenericChooseTypeColor(*this);
return a;
}
GenericChooseTypeColor::~GenericChooseTypeColor()
{
}
//set color choosen
AASetColorChosen::AASetColorChosen(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target,int _color , string toAlter):
InstantAbility(observer, id, source),color(_color), abilityToAlter(toAlter)
{
this->target = _target;
abilityAltered = NULL;
}
int AASetColorChosen::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->chooseacolor = color;
if(abilityToAlter.size())
{
AbilityFactory af(game);
abilityAltered = af.parseMagicLine(abilityToAlter, 0, NULL, _target);
if(!abilityAltered)
return 0;
abilityAltered->canBeInterrupted = false;
if(abilityAltered->oneShot)
{
abilityAltered->resolve();
SAFE_DELETE(abilityAltered);
}
else
{
abilityAltered->target = _target;
MayAbility * dontAdd = dynamic_cast<MayAbility*>(abilityAltered);
if (!dontAdd)
{
_target->cardsAbilities.push_back(abilityAltered);
for(unsigned int j = 0;j < _target->cardsAbilities.size();++j)
{
if(_target->cardsAbilities[j] == this)
_target->cardsAbilities.erase(_target->cardsAbilities.begin() + j);
}
}
abilityAltered->addToGame();
}
_target->skipDamageTestOnce = true;//some cards rely on this ability updating before damage test are run. otherwise they die before toughnes bonus applies.
}
return 1;
}
const string AASetColorChosen::getMenuText()
{
return Constants::MTGColorStrings[color];
}
AASetColorChosen * AASetColorChosen::clone() const
{
return NEW AASetColorChosen(*this);
}
AASetColorChosen::~AASetColorChosen()
{
}
//set type choosen
AASetTypeChosen::AASetTypeChosen(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target,int _type ,string _menu,string toAlter):
InstantAbility(observer, id, source),type(_type), abilityToAlter(toAlter), menutext(_menu)
{
this->target = _target;
abilityAltered = NULL;
}
int AASetTypeChosen::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
string typeChoosen = menutext;
_target->chooseasubtype = typeChoosen;
if(abilityToAlter.size())
{
AbilityFactory af(game);
abilityAltered = af.parseMagicLine(abilityToAlter, 0, NULL, _target);
if(abilityAltered->oneShot)
{
abilityAltered->resolve();
SAFE_DELETE(abilityAltered);
}
else
{
abilityAltered->target = _target;
MayAbility * dontAdd = dynamic_cast<MayAbility*>(abilityAltered);
if (!dontAdd)
{
_target->cardsAbilities.push_back(abilityAltered);
for(unsigned int j = 0;j < _target->cardsAbilities.size();++j)
{
if(_target->cardsAbilities[j] == this)
_target->cardsAbilities.erase(_target->cardsAbilities.begin() + j);
}
}
abilityAltered->addToGame();
}
_target->skipDamageTestOnce = true;//some cards rely on this ability updating before damage test are run. otherwise they die before toughnes bonus applies.
}
return 1;
}
const string AASetTypeChosen::getMenuText()
{
return menutext.c_str();
}
AASetTypeChosen * AASetTypeChosen::clone() const
{
return NEW AASetTypeChosen(*this);
}
AASetTypeChosen::~AASetTypeChosen()
{
}
//
//choosing a type or color
GenericFlipACoin::GenericFlipACoin(GameObserver* observer, int id, MTGCardInstance * source, Targetable *,string _toAdd, ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0), baseAbility(_toAdd)
{
this->GetId();
setCoin = NULL;
}
int GenericFlipACoin::resolve()
{
if (!target)
return 0;
vector<MTGAbility*>selection;
for (int i = 0; i <2; ++i)
{
setCoin = NEW AASetCoin(game, game->mLayers->actionLayer()->getMaxId(), source,(MTGCardInstance*)target, i, baseAbility);
MTGAbility * set = setCoin->clone();
set->oneShot = true;
selection.push_back(set);
SAFE_DELETE(setCoin);
}
if(selection.size())
{
MTGAbility * a1 = NEW MenuAbility(game, this->GetId(), target, source,false,selection);
game->mLayers->actionLayer()->currentActionCard = (MTGCardInstance *)target;
a1->resolve();
}
return 1;
}
const string GenericFlipACoin::getMenuText()
{
return "Flip A Coin";
}
GenericFlipACoin * GenericFlipACoin::clone() const
{
GenericFlipACoin * a = NEW GenericFlipACoin(*this);
return a;
}
GenericFlipACoin::~GenericFlipACoin()
{
}
//set color choosen
AASetCoin::AASetCoin(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target,int _side , string toAlter):
InstantAbility(observer, id, source),side(_side), abilityToAlter(toAlter)
{
this->target = _target;
abilityAltered = NULL;
}
int AASetCoin::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->coinSide = side;
int flip = game->getRandomGenerator()->random() % 2;
vector<string>Win = parseBetween(abilityToAlter,"winability "," winabilityend");
if(Win.size())
{
abilityWin = Win[1];
}
vector<string>Lose = parseBetween(abilityToAlter,"loseability "," loseabilityend");
if(Lose.size())
{
abilityLose = Lose[1];
}
if(abilityWin.size() && flip == side)
{
AbilityFactory af(game);
abilityAltered = af.parseMagicLine(abilityWin, 0, NULL, _target);
abilityAltered->canBeInterrupted = false;
if(abilityAltered->oneShot)
{
abilityAltered->resolve();
SAFE_DELETE(abilityAltered);
}
else
{
abilityAltered->addToGame();
}
MTGAbility * message = NEW MTGEventText(game,this->GetId(), source, "You Won The Flip");
message->oneShot = true;
message->addToGame();
}
else if(abilityWin.size() && !abilityLose.size())
{
MTGAbility * message = NEW MTGEventText(game,this->GetId(), source, "You Lost The Flip");
message->oneShot = true;
message->addToGame();
}
else if(abilityLose.size() && flip != side)
{
AbilityFactory af(game);
abilityAltered = af.parseMagicLine(abilityLose, 0, NULL, _target);
abilityAltered->canBeInterrupted = false;
if(abilityAltered->oneShot)
{
abilityAltered->resolve();
SAFE_DELETE(abilityAltered);
}
else
{
abilityAltered->addToGame();
}
MTGAbility * message = NEW MTGEventText(game,this->GetId(), source, "You Lost The Flip");
message->oneShot = true;
message->addToGame();
}
else if(abilityLose.size())
{
MTGAbility * message = NEW MTGEventText(game,this->GetId(), source, "You Won The Flip");
message->oneShot = true;
message->addToGame();
}
_target->skipDamageTestOnce = true;
return 1;
}
const string AASetCoin::getMenuText()
{
if(side == 1)
return "Tails";
return "Heads";
}
AASetCoin * AASetCoin::clone() const
{
return NEW AASetCoin(*this);
}
AASetCoin::~AASetCoin()
{
}
//paying for an ability as an effect but as a cost
GenericPaidAbility::GenericPaidAbility(GameObserver* observer, int id, MTGCardInstance * source,
Targetable * target, string _newName, string _castRestriction, string mayCost, string _toAdd, bool asAlternate, ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0),
newName(_newName), restrictions(_castRestriction), baseCost(mayCost), baseAbilityStr(_toAdd), asAlternate(asAlternate)
{
this->GetId();
baseAbility = NULL;
optionalCost = NULL;
}
int GenericPaidAbility::resolve()
{
if (!target)
return 0;
if (restrictions.size())
{
AbilityFactory af(game);
int checkCond = af.parseCastRestrictions(source,source->controller(),restrictions);
if(!checkCond)
{
return 0;
}
}
AbilityFactory Af(game);
vector<string> baseAbilityStrSplit = split(baseAbilityStr,'?');
vector<MTGAbility*> selection;
MTGAbility * nomenuAbility = NULL;
bool nomenu = false;
if (baseAbilityStrSplit.size() > 1)
{
baseAbility = Af.parseMagicLine(baseAbilityStrSplit[0], this->GetId(), NULL, source);
baseAbility->target = target;
optionalCost = ManaCost::parseManaCost(baseCost, NULL, source);
/*// hacky way to produce better MenuText
AAFakeAbility* isFake = dynamic_cast< AAFakeAbility* >( baseAbility );
size_t findPayN = isFake->named.find(" {value} mana");
if (isFake && findPayN != string::npos) {
stringstream parseN;
parseN << optionalCost->getCost(Constants::MTG_COLOR_ARTIFACT);
isFake->named.replace(findPayN + 1, 7, parseN.str());
}//commented out, it crashes cards with recover ability*/
MTGAbility * set = baseAbility->clone();
set->oneShot = true;
selection.push_back(set);
SAFE_DELETE(baseAbility);
baseAbility = Af.parseMagicLine(baseAbilityStrSplit[1], this->GetId(), NULL, source);
baseAbility->target = target;
set = baseAbility->clone();
set->oneShot = true;
selection.push_back(set);
}
else
{
//dangerous code below, parse a string line that might not exist. baseAbilityStrSplit[0]
//you either have a string and do stuff, or dont and leave the ability
//not fixing this since its been heavily modified from the orginal implementation.
nomenu = true;
baseAbility = Af.parseMagicLine(baseAbilityStrSplit[0], this->GetId(), NULL, source);
baseAbility->target = target;
optionalCost = ManaCost::parseManaCost(baseCost, NULL, source);
MTGAbility * set = baseAbility->clone();
nomenuAbility = baseAbility->clone();
set->oneShot = true;
selection.push_back(set);
}
if (selection.size())
{
bool must = baseAbilityStrSplit.size() > 1 ? true : false;
//todo get increased - reduced cost if asAlternate cost to cast using castcard
if(asAlternate)
{
must = true;
//cost increase - reduce + trinisphere effect ability todo...
optionalCost = ((MTGCardInstance *)target)->computeNewCost(((MTGCardInstance *)target),optionalCost,optionalCost);
if(optionalCost->extraCosts)
{
for(unsigned int i = 0; i < optionalCost->extraCosts->costs.size();i++)
optionalCost->extraCosts->costs[i]->setSource(((MTGCardInstance *)target));
}
}
if (source && source->previous && source->basicAbilities[(int)Constants::MADNESS])
{
must = true;
optionalCost = source->computeNewCost(source->previous,optionalCost,optionalCost);
if(optionalCost->extraCosts)
{
for(unsigned int i = 0; i < optionalCost->extraCosts->costs.size();i++)
optionalCost->extraCosts->costs[i]->setSource(source);
}
}
if(asAlternate && nomenu && optionalCost->getConvertedCost() < 1)
nomenuAbility->resolve();
else
{
MenuAbility * a1 = NEW MenuAbility(game, this->GetId(), target, source, must, selection, NULL, newName);
a1->optionalCosts.push_back(NEW ManaCost(optionalCost));
game->mLayers->actionLayer()->currentActionCard = (MTGCardInstance *)target;
a1->resolve();
}
}
return 1;
}
const string GenericPaidAbility::getMenuText()
{
if (newName.size())
return newName.c_str();
return "Pay For Effect";
}
GenericPaidAbility * GenericPaidAbility::clone() const
{
GenericPaidAbility * a = NEW GenericPaidAbility(*this);
return a;
}
GenericPaidAbility::~GenericPaidAbility()
{
SAFE_DELETE(optionalCost);
SAFE_DELETE(baseAbility);
}
//saves a listed mana type until end of turn.
AManaPoolSaver::AManaPoolSaver(GameObserver* observer, int id, MTGCardInstance * source,string color, bool otherPlayer) :
MTGAbility(observer, id, source),Color(color),OtherPlayer(otherPlayer)
{
}
int AManaPoolSaver::addToGame()
{
int colorInt = Constants::GetColorStringIndex(Color.c_str());
source->controller()->poolDoesntEmpty->add(colorInt,1);
return 1;
}
int AManaPoolSaver::destroy()
{
int colorInt = Constants::GetColorStringIndex(Color.c_str());
source->controller()->poolDoesntEmpty->remove(colorInt,1);
return 1;
}
AManaPoolSaver * AManaPoolSaver::clone() const
{
AManaPoolSaver * a = NEW AManaPoolSaver(*this);
return a;
}
AManaPoolSaver::~AManaPoolSaver()
{
}
//replace drawing a card with activation of an ability
ADrawReplacer::ADrawReplacer(GameObserver* observer, int id, MTGCardInstance * source, MTGAbility * replace, bool otherPlayer) :
MTGAbility(observer, id, source),re(NULL),replacer(replace),OtherPlayer(otherPlayer)
{
}
int ADrawReplacer::addToGame()
{
SAFE_DELETE(re);
if(OtherPlayer)
re = NEW REDrawReplacement(this,source->controller()->opponent(),replacer);
else
re = NEW REDrawReplacement(this,source->controller(),replacer);
if (re)
{
game->replacementEffects->add(re);
return MTGAbility::addToGame();
}
return 0;
}
int ADrawReplacer::destroy()
{
game->replacementEffects->remove(re);
SAFE_DELETE(re);
return 1;
}
ADrawReplacer * ADrawReplacer::clone() const
{
ADrawReplacer * a = NEW ADrawReplacer(*this);
a->re = NULL;
return a;
}
ADrawReplacer::~ADrawReplacer()
{
SAFE_DELETE(re);
SAFE_DELETE(replacer);
}
//Reset Damage on creatures
AAResetDamage::AAResetDamage(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target, ManaCost * cost):
ActivatedAbility(observer, id, source, cost, 0)
{
this->target = _target;
}
int AAResetDamage::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->life = _target->toughness;
return 1;
}
const string AAResetDamage::getMenuText()
{
return "Reset Damages";
}
AAResetDamage * AAResetDamage::clone() const
{
return NEW AAResetDamage(*this);
}
//ability that resolves to do nothing.
AAFakeAbility::AAFakeAbility(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target, string _named,ManaCost * cost):
ActivatedAbility(observer, id, source, cost, 0),named(_named)
{
this->target = _target;
}
int AAFakeAbility::resolve()
{
return 1;
}
const string AAFakeAbility::getMenuText()
{
if(named.size())
return named.c_str();
return "Ability";
}
AAFakeAbility * AAFakeAbility::clone() const
{
return NEW AAFakeAbility(*this);
}
//EPIC
AAEPIC::AAEPIC(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target, string _named,ManaCost * cost, bool _ffield):
ActivatedAbility(observer, id, source, cost, 0),named(_named),FField(_ffield)
{
this->target = _target;
}
int AAEPIC::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
if(FField)
_target->controller()->forcefield = 1;
else
_target->controller()->epic = 1;
return 1;
}
const string AAEPIC::getMenuText()
{
if(named.size())
return named.c_str();
return "EPIC";
}
AAEPIC * AAEPIC::clone() const
{
return NEW AAEPIC(*this);
}
// Fizzler
AAFizzler::AAFizzler(GameObserver* observer, int _id, MTGCardInstance * card, Spell * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, card, _cost, 0)
{
aType = MTGAbility::STANDARD_FIZZLER;
target = _target;
// by default we put the spell to graveyard after fizzling
fizzleMode = ActionStack::PUT_IN_GRAVEARD;
}
int AAFizzler::resolve()
{
ActionStack * stack = game->mLayers->stackLayer();
//the next section helps Ai correctly recieve its targets for this effect
if (!target && source->target)
{
//ai is casting a spell from its hand to fizzle.
target = stack->getActionElementFromCard(source->target);
}
else if(MTGCardInstance * cTarget = dynamic_cast<MTGCardInstance *>(target))
{
//ai targeted using an ability on a card to fizzle.
target = stack->getActionElementFromCard(cTarget);
}
Spell * sTarget = (Spell *) target;
MTGCardInstance* sCard = NULL;
if (sTarget)
sCard = sTarget->source;
if (!sCard || !sTarget || sCard->has(Constants::NOFIZZLE))
return 0;
if (source->alias == 111057 && sTarget)//Draining Whelk
{
for (int j = sTarget->cost->getConvertedCost(); j > 0; j--)
{
source->counters->addCounter(1,1);
}
}
stack->Fizzle(sTarget, fizzleMode);
return 1;
}
const string AAFizzler::getMenuText()
{
return "Fizzle";
}
AAFizzler* AAFizzler::clone() const
{
return NEW AAFizzler(*this);
}
// BanishCard implementations
// Bury
AABuryCard::AABuryCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(observer, _id, _source)
{
target = _target;
andAbility = NULL;
}
int AABuryCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
_target->bury();
while(_target->next)
_target = _target->next;
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = _target;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
return 1;
}
return 0;
}
const string AABuryCard::getMenuText()
{
if(menu.size())
return menu.c_str();
return "Bury";
}
AABuryCard * AABuryCard::clone() const
{
AABuryCard * a = NEW AABuryCard(*this);
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AABuryCard::~AABuryCard()
{
SAFE_DELETE(andAbility);
}
// Destroy
AADestroyCard::AADestroyCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(observer, _id, _source)
{
target = _target;
andAbility = NULL;
}
int AADestroyCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
_target->destroy();
while(_target->next)
_target = _target->next;
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = _target;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
return 1;
}
return 0;
}
const string AADestroyCard::getMenuText()
{
return "Destroy";
}
AADestroyCard * AADestroyCard::clone() const
{
AADestroyCard * a = NEW AADestroyCard(*this);
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AADestroyCard::~AADestroyCard()
{
SAFE_DELETE(andAbility);
}
// Sacrifice
AASacrificeCard::AASacrificeCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(observer, _id, _source)
{
target = _target;
andAbility = NULL;
}
int AASacrificeCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
Player * p = _target->controller();
MTGCardInstance * beforeCard = _target;
WEvent * e;
if(!_target->isToken)
e = NEW WEventCardSacrifice(beforeCard,_target);
else
e = NEW WEventCardSacrifice(beforeCard,_target,true);
p->game->putInGraveyard(_target);
while(_target->next)
_target = _target->next;
game->receiveEvent(e);
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = _target;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
return 1;
}
return 0;
}
const string AASacrificeCard::getMenuText()
{
return "Sacrifice";
}
AASacrificeCard * AASacrificeCard::clone() const
{
AASacrificeCard * a = NEW AASacrificeCard(*this);
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AASacrificeCard::~AASacrificeCard()
{
SAFE_DELETE(andAbility);
}
// Discard
AADiscardCard::AADiscardCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
ActivatedAbility(observer, _id, _source)
{
target = _target;
andAbility = NULL;
}
int AADiscardCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
Player * p = _target->controller();
WEvent * e = NEW WEventCardDiscard(_target);
game->receiveEvent(e);
p->game->putInGraveyard(_target);
while(_target->next)
_target = _target->next;
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = _target;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
return 1;
}
return 0;
}
const string AADiscardCard::getMenuText()
{
return "Discard";
}
AADiscardCard * AADiscardCard::clone() const
{
AADiscardCard * a = NEW AADiscardCard(*this);
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AADiscardCard::~AADiscardCard()
{
SAFE_DELETE(andAbility);
}
//
AADrawer::AADrawer(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, string nbcardsStr,
int who, bool noreplace) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who), nbcardsStr(nbcardsStr),noReplace(noreplace)
{
aType = MTGAbility::STANDARD_DRAW;
}
int AADrawer::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
WParsedInt numCards(nbcardsStr, NULL, source);
WEvent * e = NEW WEventDraw(player, numCards.getValue(),this);
if(!noReplace)
e = game->replacementEffects->replace(e);
if(e)
{
game->mLayers->stackLayer()->addDraw(player, numCards.getValue());
game->mLayers->stackLayer()->resolve();
for(int i = numCards.getValue(); i > 0;i--)
{
player->drawCounter += 1;
if ((game->turn < 1) && game->getCurrentGamePhase() == MTG_PHASE_FIRSTMAIN
&& game->currentPlayer->game->inPlay->nb_cards == 0 && game->currentPlayer->game->graveyard->nb_cards == 0
&& game->currentPlayer->game->exile->nb_cards == 0 && game->currentlyActing() == (Player*)game->currentPlayer) //1st Play Check
{
game->currentPlayer->drawCounter = 0;//Reset drawCounter for pre-game draw
}
WEvent * e = NEW WEventcardDraw(player, 1);
game->receiveEvent(e);
}
}
SAFE_DELETE(e);
}
return 1;
}
int AADrawer::getNumCards()
{
WParsedInt numCards(nbcardsStr, NULL, source);
return numCards.getValue();
}
const string AADrawer::getMenuText()
{
return "Draw";
}
AADrawer * AADrawer::clone() const
{
return NEW AADrawer(*this);
}
// AAFrozen: Prevent a card from untapping during next untap phase
AAFrozen::AAFrozen(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, bool tap, ManaCost * _cost) :
ActivatedAbility(observer, id, card, _cost, 0)
{
target = _target;
freeze = tap;
}
int AAFrozen::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next; //This is for cards such as rampant growth
if (freeze)
{
_target->tap();//easier to manage for cards that allow you to tap and also freeze.
}
_target->frozen += 1;
}
return 1;
}
const string AAFrozen::getMenuText()
{
return "Freeze";
}
AAFrozen * AAFrozen::clone() const
{
return NEW AAFrozen(*this);
}
// chose a new target for an aura or enchantment and equip it note: VERY basic right now.
AANewTarget::AANewTarget(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,bool retarget, ManaCost * _cost, bool reequip, bool newhook) :
ActivatedAbility(observer, id, card, _cost, 0),retarget(retarget),reequip(reequip),newhook(newhook)
{
target = _target;
}
int AANewTarget::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(retarget)
{
_target = source;
source = (MTGCardInstance *) target;
}
if (_target && !reequip)
{
while (_target->next)
_target = _target->next;
_target->controller()->game->putInZone(_target, _target->currentZone,
_target->owner->game->exile);
_target = _target->next;
MTGCardInstance * refreshed = source->controller()->game->putInZone(_target,_target->currentZone,source->controller()->game->battlefield);
Spell * reUp = NEW Spell(game, refreshed);
if(reUp->source->hasSubtype(Subtypes::TYPE_AURA))
{
reUp->source->target = source;
reUp->resolve();
}
if(_target->hasSubtype(Subtypes::TYPE_EQUIPMENT))
{
reUp->resolve();
for (size_t i = 1; i < game->mLayers->actionLayer()->mObjects.size(); i++)
{
MTGAbility * a = ((MTGAbility *) game->mLayers->actionLayer()->mObjects[i]);
AEquip * eq = dynamic_cast<AEquip*> (a);
if (eq && eq->source == reUp->source)
{
((AEquip*)a)->unequip();
((AEquip*)a)->equip(source);
}
}
}
delete reUp;
if(retarget)
{
target = source;
source = _target;
}
}
if (_target && _target->currentZone == _target->controller()->game->battlefield && reequip)
{
if(!newhook)
{
_target = source;
source = (MTGCardInstance *) target;
}
else
{
while (_target->next)
_target = _target->next;
}
if(_target->hasSubtype(Subtypes::TYPE_EQUIPMENT))
{
for (size_t i = 1; i < game->mLayers->actionLayer()->mObjects.size(); i++)
{
MTGAbility * a = ((MTGAbility *) game->mLayers->actionLayer()->mObjects[i]);
AEquip * eq = dynamic_cast<AEquip*> (a);
if (eq && eq->source == _target)
{
((AEquip*)a)->unequip();
((AEquip*)a)->equip(source);
}
}
}
if(!newhook)
{
target = source;
source = _target;
}
}
return 1;
}
const string AANewTarget::getMenuText()
{
return "New Target";
}
AANewTarget * AANewTarget::clone() const
{
AANewTarget * a = NEW AANewTarget(*this);
a->oneShot = 1;
return a;
}
// morph a card
AAMorph::AAMorph(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, id, card, _cost, restrictions)
{
target = _target;
}
int AAMorph::resolve()
{
MTGCardInstance * Morpher = (MTGCardInstance*)source;
if(!Morpher->isMorphed && !Morpher->morphed && Morpher->turningOver)
return 0;
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next;
AbilityFactory af(game);
_target->morphed = false;
_target->isMorphed = false;
_target->turningOver = true;
af.getAbilities(&currentAbilities, NULL, _target, 0);
for (size_t i = 0; i < currentAbilities.size(); ++i)
{
MTGAbility * a = currentAbilities[i];
a->source = (MTGCardInstance *) _target;
if( a && dynamic_cast<AAMorph *> (a))
{
a->removeFromGame();
game->removeObserver(a);
}
if (a)
{
if (a->oneShot)
{
a->resolve();
delete (a);
}
else
{
a->addToGame();
MayAbility * dontAdd = dynamic_cast<MayAbility*>(a);
if(!dontAdd)
{
_target->cardsAbilities.push_back(a);
}
}
}
}
currentAbilities.clear();
testDestroy();
}
return 1;
}
int AAMorph::testDestroy()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(target)
{
if(_target->turningOver && !_target->isMorphed && !_target->morphed)
{
game->removeObserver(this);
return 1;
}
}
return 0;
}
const string AAMorph::getMenuText()
{
return "Morph";
}
AAMorph * AAMorph::clone() const
{
AAMorph * a = NEW AAMorph(*this);
a->forceDestroy = 1;
return a;
}
//Melded From Setter
AAMeldFrom::AAMeldFrom(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, string MeldedName) :
ActivatedAbility(observer, id, card, 0), _MeldedName(MeldedName)
{
target = _target;
// aType = MTGAbility::Melder;
}
int AAMeldFrom::resolve()
{
source->MeldedFrom = _MeldedName;
return 1;
}
const string AAMeldFrom::getMenuText()
{
return "Melded From";
}
AAMeldFrom * AAMeldFrom::clone() const
{
return NEW AAMeldFrom(*this);
}
//Melding
AAMeld::AAMeld(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, string MeldedName) :
ActivatedAbility(observer, id, card, 0), _MeldedName(MeldedName)
{
target = _target;
// aType = MTGAbility::Melder;
}
int AAMeld::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
if (_target && _target->controller() == source->controller() && _target->owner == source->owner && !_target->isToken && !source->isToken)
{
source->controller()->game->putInExile(source);
_target->controller()->game->putInExile(_target);
source->next->controller()->game->putInZone(source->next, source->next->currentZone, source->next->controller()->game->temp);
_target->next->controller()->game->putInZone(_target->next, _target->next->currentZone, _target->next->controller()->game->temp);
MTGAbility *a = NEW AACastCard(game, game->mLayers->actionLayer()->getMaxId(), source, source, false, false, false, _MeldedName, _MeldedName, false, true);
a->oneShot = false;
a->canBeInterrupted = false;
a->addToGame();
return 1;
}
return 0;
}
const string AAMeld::getMenuText()
{
return "Meld";
}
AAMeld * AAMeld::clone() const
{
return NEW AAMeld(*this);
}
// flip a card
AAFlip::AAFlip(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,string flipStats) :
InstantAbility(observer, id, card, _target),flipStats(flipStats)
{
target = _target;
}
int AAFlip::resolve()
{
MTGCardInstance * Flipper = (MTGCardInstance*)source;
this->oneShot = true;
if(Flipper->isFlipped)
{
game->removeObserver(this);
return 0;
}
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next;
AbilityFactory af(game);
_target->isFlipped = true;
GameObserver * game = _target->getObserver();
if(flipStats.size())
{
MTGCard * fcard = MTGCollection()->getCardByName(flipStats);
if(!fcard) return 0;
MTGCardInstance * myFlip = NEW MTGCardInstance(fcard, _target->controller()->game);
_target->name = myFlip->name;
_target->setName(myFlip->name);
_target->colors = myFlip->colors;
_target->types = myFlip->types;
_target->text = myFlip->text;
_target->formattedText = myFlip->formattedText;
_target->basicAbilities = myFlip->basicAbilities;
for(unsigned int i = 0;i < _target->cardsAbilities.size();i++)
{
MTGAbility * a = dynamic_cast<MTGAbility *>(_target->cardsAbilities[i]);
if(a) game->removeObserver(a);
}
_target->cardsAbilities.clear();
_target->magicText = myFlip->magicText;
af.getAbilities(&currentAbilities, NULL, _target);
for (size_t i = 0; i < currentAbilities.size(); ++i)
{
MTGAbility * a = currentAbilities[i];
a->source = (MTGCardInstance *) _target;
if (a)
{
if (a->oneShot)
{
a->resolve();
SAFE_DELETE(a);
}
else
{
a->addToGame();
MayAbility * dontAdd = dynamic_cast<MayAbility*>(a);
if(!dontAdd)
{
_target->cardsAbilities.push_back(a);
}
}
}
}
//power
int powerMod = 0;
int toughMod = 0;
bool powerlessThanOriginal = false;
bool toughLessThanOriginal = false;
if(_target->power < _target->origpower)
{
powerMod = _target->origpower - _target->power;
powerlessThanOriginal = true;
}
else
{
powerMod =_target->power - _target->origpower;
}
//toughness
if(_target->toughness <= _target->origtoughness)
{
toughMod = _target->origtoughness - _target->toughness;
toughLessThanOriginal = true;
}
else
{
toughMod =_target->toughness - _target->origtoughness;
}
if(!_target->isCDA)
{
_target->power = powerlessThanOriginal?myFlip->power - powerMod:myFlip->power + powerMod;
_target->life = toughLessThanOriginal?myFlip->toughness - toughMod:myFlip->toughness + toughMod;
_target->toughness = toughLessThanOriginal?myFlip->toughness - toughMod:myFlip->toughness + toughMod;
_target->origpower = myFlip->origpower;
_target->origtoughness = myFlip->origtoughness;
}
SAFE_DELETE(myFlip);
_target->mPropertiesChangedSinceLastUpdate = true;
}
currentAbilities.clear();
testDestroy();
}
return 1;
}
int AAFlip::testDestroy()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(target)
{
if(_target->isFlipped)
{
this->forceDestroy = 1;
//_target->getObserver()->removeObserver(this);
//originally added as a safegaurd to insure the ability was removed
//it's been so long and so much has changed that it appears to do nothing but cause a crash now
_target->isFlipped = false;
return 1;
}
}
return 0;
}
const string AAFlip::getMenuText()
{
string s = flipStats;
sprintf(menuText, "Transform:%s", s.c_str());
return menuText;
}
AAFlip * AAFlip::clone() const
{
AAFlip * a = NEW AAFlip(*this);
a->forceDestroy = 1;
return a;
}
// AADYNAMIC: dynamic ability builder
AADynamic::AADynamic(GameObserver* observer, int id, MTGCardInstance * card, Damageable * _target,int type,int effect,int who,int amountsource,MTGAbility * storedAbility, ManaCost * _cost) :
ActivatedAbility(observer, id, card, _cost, 0),type(type),effect(effect),who(who),amountsource(amountsource),storedAbility(storedAbility)
{
target = _target;
sourceamount = 0;
targetamount = 0;
eachother = false;
tosrc = false;
menu = "";
OriginalSrc = source;
clonedStored = NULL;
mainAbility = NULL;
}
int AADynamic::resolve()
{
Damageable * _target = (Damageable *) target;
Damageable * secondaryTarget = NULL;
if(amountsource == 2)
source = (MTGCardInstance * )_target;
switch(who)
{
case DYNAMIC_ABILITY_WHO_EACHOTHER://each other, both take the effect
eachother = true;
break;
case DYNAMIC_ABILITY_WHO_ITSELF:
source = ((MTGCardInstance *) _target);
break;
case DYNAMIC_ABILITY_WHO_TARGETCONTROLLER:
secondaryTarget = ((MTGCardInstance *) _target)->controller();
break;
case DYNAMIC_ABILITY_WHO_TARGETOPPONENT:
secondaryTarget = ((MTGCardInstance *) _target)->controller()->opponent();
break;
case DYNAMIC_ABILITY_WHO_TOSOURCE:
tosrc = true;
break;
case DYNAMIC_ABILITY_WHO_SOURCECONTROLLER:
secondaryTarget = ((MTGCardInstance *) OriginalSrc)->controller();
break;
case DYNAMIC_ABILITY_WHO_SOURCEOPPONENT:
secondaryTarget = OriginalSrc->controller()->opponent();
break;
default:
break;
}
if(amountsource == DYNAMIC_MYSELF_AMOUNT)
_target = OriginalSrc->controller();//looking at controller for amount
if(amountsource == DYNAMIC_MYFOE_AMOUNT)
_target = OriginalSrc->controller()->opponent();//looking at controllers opponent for amount
if(!_target)
return 0;
while (dynamic_cast<MTGCardInstance *>(_target) && ((MTGCardInstance *)_target)->next)
_target = ((MTGCardInstance *)_target)->next;
//find the amount variables that will be used
sourceamount = 0;
targetamount = 0;
int colored = 0;
switch(type)
{
case DYNAMIC_ABILITY_TYPE_POWER:
sourceamount = ((MTGCardInstance *) source)->getCurrentPower();
targetamount = ((MTGCardInstance *) _target)->getCurrentPower();
if(eachother )
sourceamount = ((MTGCardInstance *) source)->getCurrentPower();
break;
case DYNAMIC_ABILITY_TYPE_TOUGHNESS:
sourceamount = ((MTGCardInstance *) source)->getCurrentToughness();
targetamount = ((MTGCardInstance *) _target)->getCurrentToughness();
if(eachother )
sourceamount = ((MTGCardInstance *) source)->getCurrentToughness();
break;
case DYNAMIC_ABILITY_TYPE_MANACOST:
if(amountsource == 1)
sourceamount = ((MTGCardInstance *) source)->getManaCost()->getConvertedCost();
else
sourceamount = ((MTGCardInstance *) _target)->getManaCost()->getConvertedCost();
break;
case DYNAMIC_ABILITY_TYPE_COLORS:
for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i)
{
if (amountsource == 1 && ((MTGCardInstance *)source)->hasColor(i))
++colored;
else
if (amountsource == 2 && ((MTGCardInstance *)_target)->hasColor(i))
++colored;
}
sourceamount = colored;
break;
case DYNAMIC_ABILITY_TYPE_AGE:
{
Counter * targetCounter = NULL;
if(amountsource == 2)
{
if (((MTGCardInstance *)_target)->counters && ((MTGCardInstance *)_target)->counters->hasCounter("age", 0, 0))
{
targetCounter = ((MTGCardInstance *)_target)->counters->hasCounter("age", 0, 0);
sourceamount = targetCounter->nb;
}
}
else
{
if (((MTGCardInstance *)source)->counters && ((MTGCardInstance *)source)->counters->hasCounter("age", 0, 0))
{
targetCounter = ((MTGCardInstance *)source)->counters->hasCounter("age", 0, 0);
sourceamount = targetCounter->nb;
}
}
break;
}
case DYNAMIC_ABILITY_TYPE_CHARGE:
{
Counter * targetCounter = NULL;
if(amountsource == 2)
{
if (((MTGCardInstance *)_target)->counters && ((MTGCardInstance *)_target)->counters->hasCounter("charge", 0, 0))
{
targetCounter = ((MTGCardInstance *)_target)->counters->hasCounter("charge", 0, 0);
sourceamount = targetCounter->nb;
}
}
else
{
if (((MTGCardInstance *)source)->counters && ((MTGCardInstance *)source)->counters->hasCounter("charge", 0, 0))
{
targetCounter = ((MTGCardInstance *)source)->counters->hasCounter("charge", 0, 0);
sourceamount = targetCounter->nb;
}
}
break;
}
case DYNAMIC_ABILITY_TYPE_ONEONECOUNTERS:
{
Counter * targetCounter = NULL;
if(amountsource == 2)
{
if (((MTGCardInstance *)_target)->counters && ((MTGCardInstance *)_target)->counters->hasCounter(1, 1))
{
targetCounter = ((MTGCardInstance *)_target)->counters->hasCounter(1,1);
sourceamount = targetCounter->nb;
}
}
else
{
if (((MTGCardInstance *)source)->counters && ((MTGCardInstance *)source)->counters->hasCounter(1, 1))
{
targetCounter = ((MTGCardInstance *)source)->counters->hasCounter(1,1);
sourceamount = targetCounter->nb;
}
}
break;
}
case DYNAMIC_ABILITY_TYPE_THATMUCH:
{
sourceamount = _target->thatmuch;
break;
}
default:
break;
}
if(secondaryTarget != NULL)
_target = secondaryTarget;
if (_target)
{
while (dynamic_cast<MTGCardInstance *>(_target) && ((MTGCardInstance *)_target)->next)
_target = ((MTGCardInstance *)_target)->next;
if(sourceamount < 0)
sourceamount = 0;
if(targetamount < 0)
targetamount = 0;
std::stringstream out;
std::stringstream out2;
out << sourceamount;
string sourceamountstring = out.str();
out2 << targetamount;
string targetamountstring = out2.str();
//set values less then 0 to 0, it was reported that negitive numbers such as a creature who get -3/-3 having the power become
//negitive, if then used as the amount, would cuase weird side effects on resolves.
switch(effect)
{
case DYNAMIC_ABILITY_EFFECT_STRIKE://deal damage
{
mainAbility = NEW AADamager(game, this->GetId(), source,tosrc == true?(Targetable*)OriginalSrc:(Targetable*)_target,sourceamountstring);
activateMainAbility(mainAbility,source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target);
if(eachother)
{
mainAbility = NEW AADamager(game, this->GetId(), (MTGCardInstance*)_target,(Targetable*)OriginalSrc,targetamountstring);
activateMainAbility(mainAbility,source,OriginalSrc);
}
return 1;
break;
}
case DYNAMIC_ABILITY_EFFECT_DRAW://draw cards
{
mainAbility = NEW AADrawer(game, this->GetId(), source,_target,NULL, sourceamountstring);
return activateMainAbility(mainAbility,source,_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_LIFEGAIN://gain life
{
mainAbility = NEW AALifer(game, this->GetId(), source,_target, sourceamountstring);
return activateMainAbility(mainAbility,source,_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_PUMPPOWER://pump power
{
mainAbility = NEW PTInstant(game, this->GetId(), source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target,NEW WParsedPT(sourceamount,0));
return activateMainAbility(mainAbility,source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_PUMPTOUGHNESS://pump toughness
{
mainAbility = NEW PTInstant(game, this->GetId(), source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target,NEW WParsedPT(0,sourceamount));
return activateMainAbility(mainAbility,source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_PUMPBOTH://pump both
{
mainAbility = NEW PTInstant(game, this->GetId(), source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target,NEW WParsedPT(sourceamount,sourceamount));
return activateMainAbility(mainAbility,source,tosrc == true?OriginalSrc:(MTGCardInstance*)_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_LIFELOSS://lose life
{
string altered = "-";
altered.append(sourceamountstring);
mainAbility = NEW AALifer(game, this->GetId(), source,_target, altered);
return activateMainAbility(mainAbility,source,_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_DEPLETE://deplete cards
{
mainAbility = NEW AADepleter(game, this->GetId(), source,_target, sourceamountstring);
return activateMainAbility(mainAbility,source,_target);
break;
}
case DYNAMIC_ABILITY_EFFECT_COUNTERSONEONE:
{
if(!dynamic_cast<MTGCardInstance *>(_target))
_target = OriginalSrc;
for(int j = 0;j < sourceamount;j++)
((MTGCardInstance*)_target)->counters->addCounter(1,1);
break;
}
default:
return 0;
}
}
return 0;
}
int AADynamic::activateMainAbility(MTGAbility * toActivate,MTGCardInstance * , Damageable *)
{
if(storedAbility)
activateStored();
if(!toActivate)
return 0;
if(PTInstant * a = dynamic_cast<PTInstant *>(toActivate))
{
a->addToGame();
return 1;
}
toActivate->oneShot = true;
toActivate->forceDestroy = 1;
toActivate->resolve();
SAFE_DELETE(toActivate);
return 1;
}
int AADynamic::activateStored()
{
clonedStored = storedAbility->clone();
clonedStored->target = target;
if (clonedStored->oneShot)
{
clonedStored->resolve();
delete (clonedStored);
}
else
{
clonedStored->addToGame();
}
return 1;
}
const string AADynamic::getMenuText()
{
if (menu.size())
{
return menu.c_str();
}
switch(type)
{
case 0:
menu.append("Power");
break;
case 1:
menu.append("Tough");
break;
case 2:
menu.append("Mana");
break;
case 3:
menu.append("color");
break;
case 4:
menu.append("Elder");
break;
case 5:
menu.append("Charged");
break;
case 6:
menu.append("Counter");
break;
case 7:
menu.append("That Many ");
break;
default:
break;
}
switch(effect)
{
case 0:
menu.append("Strike");
break;
case 1:
menu.append("Draw");
break;
case 2:
menu.append("Life");
break;
case 3:
menu.append("Pump");
break;
case 4:
menu.append("Fortify");
break;
case 5:
menu.append("Buff");
break;
case 6:
menu.append("Drain");
break;
case 7:
menu.append("Deplete!");
break;
case 8:
menu.append("Counters!");
break;
default:
break;
}
return menu.c_str();
}
AADynamic * AADynamic::clone() const
{
AADynamic * a = NEW AADynamic(*this);
a->storedAbility = storedAbility? storedAbility->clone() : NULL;
return a;
}
AADynamic::~AADynamic()
{
SAFE_DELETE(storedAbility);
}
//AALifer
AALifer::AALifer(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, string life_s, ManaCost * _cost, int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who),life_s(life_s)
{
aType = MTGAbility::LIFER;
}
int AALifer::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (!_target)
return 0;
WParsedInt life(life_s, NULL, source);
if (_target->type_as_damageable == Damageable::DAMAGEABLE_MTGCARDINSTANCE)
{
_target = ((MTGCardInstance *) _target)->controller();
}
Player *player = (Player*)_target;
player->gainOrLoseLife(life.getValue());
return 1;
}
int AALifer::getLife()
{
WParsedInt life(life_s, NULL, source);
return life.getValue();
}
const string AALifer::getMenuText()
{
if(getLife() < 0)
return "Life Loss";
return "Life";
}
AALifer * AALifer::clone() const
{
return NEW AALifer(*this);
}
//players modify hand size
AModifyHand::AModifyHand(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, string hand, int who) :
AbilityTP(observer, _id, _source, _target, who), hand(hand)
{
}
int AModifyHand::addToGame()
{
Damageable * _target = (Damageable *) getTarget();
Player * p = getPlayerFromDamageable(_target);
if (!p)
return 0;
WParsedInt handmodifier(hand, NULL, source);
p->handmodifier += handmodifier.getValue();
return MTGAbility::addToGame();
}
int AModifyHand::destroy()
{
Damageable * _target = (Damageable *) getTarget();
Player * p = getPlayerFromDamageable(_target);
if (!p)
return 0;
WParsedInt handmodifier(hand, NULL, source);
p->handmodifier -= handmodifier.getValue();
return 1;
}
const string AModifyHand::getMenuText()
{
return "Modify Hand Size";
}
AModifyHand * AModifyHand::clone() const
{
return NEW AModifyHand(*this);
}
//players max hand size
AASetHand::AASetHand(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, int hand, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who), hand(hand)
{
}
int AASetHand::resolve()
{
Damageable * _target = (Damageable *) getTarget();
Player * p = getPlayerFromDamageable(_target);
if (!p)
return 0;
p->handsize = hand;
return 1;
}
const string AASetHand::getMenuText()
{
return "Set Hand Size";
}
AASetHand * AASetHand::clone() const
{
return NEW AASetHand(*this);
}
//Lifeset
AALifeSet::AALifeSet(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, WParsedInt * life, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who), life(life)
{
}
int AALifeSet::resolve()
{
Damageable * _target = (Damageable *) getTarget();
Player * p = getPlayerFromDamageable(_target);
if (!p)
return 0;
int lifeDiff = life->getValue() - p->life ;
p->gainOrLoseLife(lifeDiff);
return 1;
}
const string AALifeSet::getMenuText()
{
return "Set Life";
}
AALifeSet * AALifeSet::clone() const
{
AALifeSet * a = NEW AALifeSet(*this);
a->life = NEW WParsedInt(*(a->life));
return a;
}
AALifeSet::~AALifeSet()
{
SAFE_DELETE(life);
}
//AACloner
//cloning...this makes a token thats a copy of the target.
AACloner::AACloner(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost, int who,
string abilitiesStringList,string TypesList) :
ActivatedAbility(observer, _id, _source, _cost, 0), who(who)
{
aType = MTGAbility::CLONING;
target = _target;
source = _source;
if (abilitiesStringList.size() > 0)
{
PopulateAbilityIndexVector(awith, abilitiesStringList);
PopulateColorIndexVector(colors, abilitiesStringList);
}
if (TypesList.size())
{
PopulateSubtypesIndexVector(typesToAdd,TypesList);
}
}
int AACloner::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (!_target)
return 0;
// Use id of the card to have the same image as the original
MTGCard* clone = (_target->isToken ? _target: MTGCollection()->getCardById(_target->getId()));
// If its a copier then copy what it is
if(_target->isACopier)
clone = _target;
Player * targetPlayer = who == 1 ? source->controller()->opponent() : source->controller();
int tokenize = 1;//tokenizer support for cloning
if (targetPlayer->game->battlefield->hasAbility(Constants::TOKENIZER))
{
int nbcards = targetPlayer->game->battlefield->nb_cards;
for (int j = 0; j < nbcards; j++)
{
if (targetPlayer->game->battlefield->cards[j]->has(Constants::TOKENIZER))
tokenize *= 2;
}
}
for (int i = 0; i < tokenize; ++i)
{
MTGCardInstance * myClone = NEW MTGCardInstance(clone, targetPlayer->game);
targetPlayer->game->temp->addCard(myClone);
Spell * spell = NEW Spell(game, myClone);
spell->source->isToken = 1;
spell->resolve();
spell->source->fresh = 1;
spell->source->model = spell->source;
spell->source->model->data = spell->source;
//if the token doesn't have cda/dynamic pt then allow this...
if((_target->isToken) && (!_target->isCDA))
{
if(_target->pbonus > 0)
spell->source->power = _target->power - _target->pbonus;
else
spell->source->power = _target->power + abs(_target->pbonus);
if(_target->tbonus > 0)
{
spell->source->toughness = _target->toughness - _target->tbonus;
spell->source->life = _target->toughness - _target->tbonus;
}
else
{
spell->source->toughness = _target->toughness + abs(_target->tbonus);
spell->source->life = _target->toughness + abs(_target->tbonus);
}
}
list<int>::iterator it;
for (it = awith.begin(); it != awith.end(); it++)
{//there must be a layer of temporary abilities and original abilities
spell->source->basicAbilities[*it] = 1;
}
for (it = colors.begin(); it != colors.end(); it++)
{
spell->source->setColor(*it);
}
for (it = typesToAdd.begin(); it != typesToAdd.end(); it++)
{
spell->source->addType(*it);
}
spell->source->modifiedbAbi = _target->modifiedbAbi;
spell->source->basicAbilities = _target->origbasicAbilities;
delete spell;
}
return 1;
}
const string AACloner::getMenuText()
{
if (who == 1)
return "Clone For Opponent";
return "Clone";
}
ostream& AACloner::toString(ostream& out) const
{
out << "AACloner ::: with : ?" // << abilities
<< " (";
return ActivatedAbility::toString(out) << ")";
}
AACloner * AACloner::clone() const
{
return NEW AACloner(*this);
}
AACloner::~AACloner()
{
}
// Cast/Play Restriction modifier
ACastRestriction::ACastRestriction(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who) :
AbilityTP(observer, _id, card, _target, who), restrictionsScope(_restrictionsScope), value(_value), modifyExisting(_modifyExisting),zoneId(_zoneId)
{
existingRestriction = NULL;
targetPlayer = NULL;
}
int ACastRestriction::addToGame()
{
Targetable * _target = getTarget();
targetPlayer = getPlayerFromTarget(_target);
if (!targetPlayer)
return 0;
if (modifyExisting)
{
//For now the only modifying rule is the one for lands, so this is hardcoded here.
//This means that a modifying rule for anything lands will actually modify the lands rule.
//In the future, we need a way to "identify" rules that modify an existing restriction, probably by doing a comparison of the TargetChoosers
existingRestriction = targetPlayer->game->playRestrictions->getMaxPerTurnRestrictionByTargetChooser(restrictionsScope);
if(existingRestriction && existingRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX)
existingRestriction->maxPerTurn += value->getValue();
}
else
{
TargetChooser * _tc = restrictionsScope->clone();
existingRestriction = NEW MaxPerTurnRestriction(_tc, value->getValue(), MTGGameZone::intToZone(zoneId, targetPlayer));
targetPlayer->game->playRestrictions->addRestriction(existingRestriction);
}
AbilityTP::addToGame();
return 1;
}
int ACastRestriction::destroy()
{
if (!existingRestriction)
return 0;
if (modifyExisting)
{
if(existingRestriction->maxPerTurn != MaxPerTurnRestriction::NO_MAX)
existingRestriction->maxPerTurn -= value->getValue();
}
else
{
targetPlayer->game->playRestrictions->removeRestriction(existingRestriction);
SAFE_DELETE(existingRestriction);
}
return 1;
}
const string ACastRestriction::getMenuText()
{
if (modifyExisting)
return "Additional Lands"; //hardoced because only the lands rule allows to modify existing rule for now
return "Cast Restriction";
}
ACastRestriction * ACastRestriction::clone() const
{
ACastRestriction * a = NEW ACastRestriction(*this);
a->value = NEW WParsedInt(*(a->value));
a->restrictionsScope = restrictionsScope->clone();
return a;
}
ACastRestriction::~ACastRestriction()
{
SAFE_DELETE(value);
SAFE_DELETE(restrictionsScope);
}
AInstantCastRestrictionUEOT::AInstantCastRestrictionUEOT(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who) :
InstantAbilityTP(observer, _id, card, _target, who)
{
ability = NEW ACastRestriction(observer, _id, card, _target, _restrictionsScope, _value, _modifyExisting, _zoneId, who);
}
int AInstantCastRestrictionUEOT::resolve()
{
ACastRestriction * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
const string AInstantCastRestrictionUEOT::getMenuText()
{
return ability->getMenuText();
}
AInstantCastRestrictionUEOT * AInstantCastRestrictionUEOT::clone() const
{
AInstantCastRestrictionUEOT * a = NEW AInstantCastRestrictionUEOT(*this);
a->ability = this->ability->clone();
return a;
}
AInstantCastRestrictionUEOT::~AInstantCastRestrictionUEOT()
{
SAFE_DELETE(ability);
}
//AAMover
AAMover::AAMover(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, string dest,string newName, ManaCost * _cost, bool undying, bool persist) :
ActivatedAbility(observer, _id, _source, _cost, 0), destination(dest),named(newName),undying(undying),persist(persist)
{
if (_target)
target = _target;
andAbility = NULL;
}
MTGGameZone * AAMover::destinationZone(Targetable * target)
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(destination == "previousbattlefield")
{
if(_target->previousController)
return _target->previousController->inPlay();
else
return _target->controller()->inPlay();
}
return MTGGameZone::stringToZone(game, destination, source, _target);
}
int AAMover::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (target)
{
Player* p = _target->controller();
if (p)
{
MTGGameZone * fromZone = _target->getCurrentZone();
MTGGameZone * destZone = destinationZone(target);
//inplay is a special zone !
for (int i = 0; i < 2; i++)
{
if (!_target->hasSubtype(Subtypes::TYPE_AURA) && destZone == game->players[i]->game->inPlay && fromZone != game->players[i]->game->inPlay && fromZone
!= game->players[i]->opponent()->game->inPlay)
{
MTGCardInstance * copy = game->players[i]->game->putInZone(_target, fromZone, game->players[i]->game->temp);
Spell * spell = NEW Spell(game, copy);
spell->resolve();
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = spell->source;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
if(persist)
spell->source->counters->addCounter(-1,-1);
if(undying)
spell->source->counters->addCounter(1,1);
delete spell;
return 1;
}
if (destZone == game->players[i]->game->graveyard && fromZone == game->players[i]->game->hand)
{
//movers that take a card from hand and place them in graveyard are always discards. we send an event for it here.
WEvent * e = NEW WEventCardDiscard(_target);
game->receiveEvent(e);
}
}
if(_target->hasSubtype(Subtypes::TYPE_AURA) && (destZone == game->players[0]->game->inPlay || destZone == game->players[1]->game->inPlay))
{//put into play aura if there is no valid targets then it will be in its current zone
MTGAbility *a = NEW AACastCard(game, game->mLayers->actionLayer()->getMaxId(), _target, _target,false,false,false,"","Put in play",false,true);
a->oneShot = false;
a->canBeInterrupted = false;
a->addToGame();
if(andAbility && _target->next)
{//if successful target->next should be valid
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = _target->next;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
}
else
{
p->game->putInZone(_target, fromZone, destZone);
while(_target->next)
_target = _target->next;
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = _target;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
}
return 1;
}
}
return 0;
}
const string AAMover::getMenuText()
{
if(named.size())
return named.c_str();
return "Move";
}
const char* AAMover::getMenuText(TargetChooser * tc)
{
if(named.size())
return named.c_str();
MTGGameZone * dest = destinationZone();
for (int i = 0; i < 2; i++)
{
// Move card to hand
if (dest == game->players[i]->game->hand)
{
if (tc->targetsZone(game->players[i]->game->inPlay))
return "Bounce";
if (tc->targetsZone(game->players[i]->game->graveyard))
return "Reclaim";
if (tc->targetsZone(game->opponent()->game->hand))
return "Steal";
}
// Move card to graveyard
else if (dest == game->players[i]->game->graveyard)
{
if (tc->targetsZone(game->players[i]->game->inPlay))
return "Sacrifice";
if (tc->targetsZone(game->players[i]->game->hand))
return "Discard";
if (tc->targetsZone(game->opponent()->game->hand))
return "Opponent Discards";
}
// move card to library
else if (dest == game->players[i]->game->library)
{
if (tc->targetsZone(game->players[i]->game->graveyard))
return "Recycle";
return "Put in Library";
}
// move card to battlefield
else if (dest == game->players[i]->game->battlefield)
{
if (tc->targetsZone(game->players[i]->game->graveyard))
return "Reanimate";
return "Put in Play";
}
// move card into exile
else if (dest == game->players[i]->game->exile)
{
return "Exile";
}
// move card from Library
else if (tc->targetsZone(game->players[i]->game->library))
{
return "Fetch";
}
}
return "Move";
}
AAMover * AAMover::clone() const
{
AAMover * a = NEW AAMover(*this);
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AAMover::~AAMover()
{
SAFE_DELETE(andAbility);
}
//random movement of a card from zone to zone
AARandomMover::AARandomMover(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, string _tcs, string _from, string _to) :
ActivatedAbility(observer, _id, _source, NULL, 0), abilityTC(_tcs),fromZone(_from),toZone(_to)
{
if (_target)
target = _target;
}
MTGGameZone * AARandomMover::destinationZone(Targetable * target,string zone)
{
MTGCardInstance * _target = (MTGCardInstance *) target;
return MTGGameZone::stringToZone(game, zone, source, _target);
}
int AARandomMover::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (target)
{
Player* p = _target->controller();
if (p)
{
MTGGameZone * fromDest = destinationZone(target,fromZone);
MTGGameZone * toDest = destinationZone(target,toZone);
if (!fromDest->nb_cards)
return 0;
TargetChooserFactory tcf(game);
TargetChooser * rTc = tcf.createTargetChooser(abilityTC, source);
rTc->targetter = NULL;
rTc->setAllZones();
vector<MTGCardInstance*>selectedCards;
for(unsigned int i = 0; i < fromDest->cards.size();++i)
{
if(rTc->canTarget(fromDest->cards[i]))
selectedCards.push_back(fromDest->cards[i]);
}
SAFE_DELETE(rTc);
if(!selectedCards.size())
return 0;
int r = fromDest->owner->getObserver()->getRandomGenerator()->random() % (selectedCards.size());
MTGCardInstance * toMove = selectedCards[r];
//inplay is a special zone !
for (int i = 0; i < 2; i++)
{
if (toDest == game->players[i]->game->inPlay && fromDest != game->players[i]->game->inPlay && fromDest
!= game->players[i]->opponent()->game->inPlay)
{
MTGCardInstance * copy = game->players[i]->game->putInZone(toMove, fromDest, game->players[i]->game->temp);
Spell * spell = NEW Spell(game, copy);
spell->resolve();
delete spell;
return 1;
}
}
p->game->putInZone(toMove, fromDest, toDest);
return 1;
}
}
return 0;
}
const string AARandomMover::getMenuText()
{
return "Dig";
}
AARandomMover * AARandomMover::clone() const
{
AARandomMover * a = NEW AARandomMover(*this);
return a;
}
AARandomMover::~AARandomMover()
{
}
//Random Discard
AARandomDiscarder::AARandomDiscarder(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who), nbcardsStr(nbcardsStr)
{
}
int AARandomDiscarder::resolve()
{
Targetable * _target = getTarget();
Player * player = getPlayerFromTarget(_target);
if (player)
{
WParsedInt numCards(nbcardsStr, NULL, source);
for (int i = 0; i < numCards.intValue; i++)
{
player->game->discardRandom(player->game->hand, source);
}
}
return 1;
}
const string AARandomDiscarder::getMenuText()
{
return "Discard Random";
}
AARandomDiscarder * AARandomDiscarder::clone() const
{
return NEW AARandomDiscarder(*this);
}
// Shuffle
AAShuffle::AAShuffle(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who)
{
}
int AAShuffle::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
MTGLibrary * library = player->game->library;
library->shuffle();
}
return 1;
}
const string AAShuffle::getMenuText()
{
return "Shuffle";
}
AAShuffle * AAShuffle::clone() const
{
return NEW AAShuffle(*this);
}
// Mulligan
AAMulligan::AAMulligan(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who)
{
}
int AAMulligan::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
player->serumMulligan();
}
return 1;
}
const string AAMulligan::getMenuText()
{
return "Mulligan";
}
AAMulligan * AAMulligan::clone() const
{
return NEW AAMulligan(*this);
}
// Remove Mana From ManaPool
AARemoveMana::AARemoveMana(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, string manaDesc, int who) :
ActivatedAbilityTP(observer, _id, card, _target, NULL, who)
{
if (!manaDesc.size())
{
DebugTrace("ALL_ABILITIES: AARemoveMana ctor error");
return;
}
mRemoveAll = (manaDesc[0] == '*');
if (mRemoveAll)
manaDesc = manaDesc.substr(1);
mManaDesc = (manaDesc.size()) ? ManaCost::parseManaCost(manaDesc) : NULL;
}
int AARemoveMana::resolve()
{
Player * player = getPlayerFromTarget(getTarget());
if (player)
{
ManaPool * manaPool = player->getManaPool();
if (mRemoveAll)
{
if (mManaDesc) // Remove all mana Matching a description
{
for (int i = 0; i < Constants::NB_Colors; i++)
{
if (mManaDesc->hasColor(i))
manaPool->removeAll(i);
}
}
else //Remove all mana
{
if(game->getCurrentGamePhase() != MTG_PHASE_ENDOFTURN)
{
if (player->doesntEmpty->getConvertedCost() && !player->poolDoesntEmpty->getConvertedCost())
{
ManaCost * toRemove = manaPool->Diff(player->doesntEmpty);
player->getManaPool()->pay(toRemove);
delete(toRemove);
return 1;
}
else if(!player->doesntEmpty->getConvertedCost() && player->poolDoesntEmpty->getConvertedCost())
{
ManaCost * toSave = NEW ManaCost();
for(int k = Constants::MTG_COLOR_ARTIFACT; k < Constants::NB_Colors;k++)
{
if(player->poolDoesntEmpty->getCost(k))
toSave->add(k,manaPool->getCost(k));
}
player->getManaPool()->pay(manaPool->Diff(toSave));
delete(toSave);
return 1;
}
else if(player->doesntEmpty->getConvertedCost() && player->poolDoesntEmpty->getConvertedCost())
{
ManaCost * toSave = NEW ManaCost();
for(int k = Constants::MTG_COLOR_ARTIFACT; k < Constants::NB_Colors;k++)
{
if(player->poolDoesntEmpty->getCost(k))
{
toSave->add(k,manaPool->getCost(k));//save the whole amount of k;
}
else if(player->doesntEmpty->getCost(k))
{
toSave->add(k,player->doesntEmpty->getCost(k));//save the amount of doesnt empty
}
}
player->getManaPool()->pay(manaPool->Diff(toSave));//remove the manacost equal to the difference of toSave and the manapool.
delete(toSave);
return 1;
}
manaPool->Empty();
}
else
manaPool->Empty();
}
}
else //remove a "standard" mana Description
{
((ManaCost *)manaPool)->remove(mManaDesc); //why do I have to cast here?
}
}
return 1;
}
const string AARemoveMana::getMenuText()
{
if (mRemoveAll && !mManaDesc)
return "Empty Manapool";
return "Remove Mana";
}
AARemoveMana * AARemoveMana::clone() const
{
AARemoveMana * a = NEW AARemoveMana(*this);
a->mManaDesc = mManaDesc ? NEW ManaCost(mManaDesc) : NULL;
return a;
}
AARemoveMana::~AARemoveMana()
{
SAFE_DELETE(mManaDesc);
}
//Bestow
ABestow::ABestow(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, id, card, _cost, 0)
{
target = _target;
aType = MTGAbility::TAPPER;
_card = card;
}
int ABestow::resolve()
{
if (target)
{
if (_card->hasType("creature"))
{
_card->removeType("creature");
_card->addType("aura");
}
_card->target = (MTGCardInstance*)target;
_card->isBestowed = true;
}
return 1;
}
const string ABestow::getMenuText()
{
return "Bestow";
}
ABestow * ABestow::clone() const
{
return NEW ABestow(*this);
}
//Tapper
AATapper::AATapper(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost, bool sendNoEvent) :
ActivatedAbility(observer, id, card, _cost, 0),_sendNoEvent(sendNoEvent)
{
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(_sendNoEvent);
}
return 1;
}
const string AATapper::getMenuText()
{
return "Tap";
}
AATapper * AATapper::clone() const
{
return NEW AATapper(*this);
}
//AA Untapper
AAUntapper::AAUntapper(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost) :
ActivatedAbility(observer, id, card, _cost, 0)
{
target = _target;
aType = MTGAbility::UNTAPPER;
}
int AAUntapper::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next; //This is for cards such as rampant growth
_target->untap();
}
return 1;
}
const string AAUntapper::getMenuText()
{
return "Untap";
}
AAUntapper * AAUntapper::clone() const
{
return NEW AAUntapper(*this);
}
AAWhatsMax::AAWhatsMax(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance *, ManaCost * _cost, int value) :
ActivatedAbility(observer, id, card, _cost, 0), value(value)
{
}
int AAWhatsMax::resolve()
{
if (source)
{
source->MaxLevelUp = value;
source->isLeveler = 1;
}
return 1;
}
AAWhatsMax * AAWhatsMax::clone() const
{
return NEW AAWhatsMax(*this);
}
//set X value
AAWhatsX::AAWhatsX(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance *, int value, MTGAbility * _costRule) :
ActivatedAbility(observer, id, card, NULL, 0), value(value),costRule(_costRule)
{
}
int AAWhatsX::resolve()
{
if (source)
{
source->setX = value;
}
costRule->reactToClick(source);
return 1;
}
AAWhatsX * AAWhatsX::clone() const
{
return NEW AAWhatsX(*this);
}
//count objects on field before doing an effect
AACountObject::AACountObject(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance *, ManaCost * _cost, string value) :
ActivatedAbility(observer, id, card, _cost, 0), value(value)
{
}
int AACountObject::resolve()
{
if (source)
{
int amount = 0;
WParsedInt * use = NEW WParsedInt(value, NULL, source);
amount = use->getValue();
source->CountedObjects = amount;
SAFE_DELETE(use);
}
return 1;
}
AACountObject * AACountObject::clone() const
{
return NEW AACountObject(*this);
}
// Win Game
AAWinGame::AAWinGame(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, int who) :
ActivatedAbilityTP(observer, _id, card, _target, _cost, who)
{
}
int AAWinGame::resolve()
{
Player * p = getPlayerFromDamageable((Damageable *) getTarget());
if (!p)
return 0;
bool canwin = true;
MTGGameZone * z = p->opponent()->game->inPlay;
int nbcards = z->nb_cards;
for (int i = 0; i < nbcards; i++)
{
MTGCardInstance * c = z->cards[i];
if (c->has(Constants::CANTLOSE))
{
canwin = false;
break;
}
}
if (canwin)
{
MTGGameZone * k = p->game->inPlay;
int onbcards = k->nb_cards;
for (int m = 0; m < onbcards; ++m)
{
MTGCardInstance * e = k->cards[m];
if (e->has(Constants::CANTWIN))
{
canwin = false;
break;
}
}
}
if (canwin)
{
game->setLoser(p->opponent());
}
return 1;
}
const string AAWinGame::getMenuText()
{
return "Win Game";
}
AAWinGame * AAWinGame::clone() const
{
return NEW AAWinGame(*this);
}
//Generic Abilities
//a new affinity
ANewAffinity::ANewAffinity(GameObserver* observer, int _id, MTGCardInstance * _source, string Tc, string mana) :
MTGAbility(observer, _id, _source), tcString(Tc), manaString(mana)
{
}
void ANewAffinity::Update(float)
{
testDestroy();
return;
}
int ANewAffinity::testDestroy()
{
if(this->source->isInPlay(game))
return 1;
return 0;
}
ANewAffinity * ANewAffinity::clone() const
{
return NEW ANewAffinity(*this);
}
//IfThenEffect
IfThenAbility::IfThenAbility(GameObserver* observer, int _id, MTGAbility * delayedAbility, MTGAbility * delayedElseAbility, MTGCardInstance * _source, Targetable * _target, int type,string Cond) :
InstantAbility(observer, _id, _source),delayedAbility(delayedAbility),delayedElseAbility(delayedElseAbility), type(type),Cond(Cond)
{
target = _target;
}
int IfThenAbility::resolve()
{
MTGCardInstance * card = (MTGCardInstance*)source;
AbilityFactory af(game);
Targetable* aTarget = (Targetable*)target;
int checkCond = af.parseCastRestrictions(card,card->controller(),Cond);
if(Cond.find("cantargetcard(") != string::npos)
{
TargetChooser * condTc = NULL;
vector<string>splitTarget = parseBetween(Cond, "card(", ")");
if (splitTarget.size())
{
TargetChooserFactory tcf(game);
condTc = tcf.createTargetChooser(splitTarget[1], source);
condTc->targetter = NULL;
if(aTarget)
checkCond = condTc->canTarget(aTarget);
SAFE_DELETE(condTc);
}
}
MTGAbility * a1 = NULL;
if((checkCond && type == 1)||(!checkCond && type == 2))
{
a1 = delayedAbility->clone();
}
else if(delayedElseAbility)
{
a1 = delayedElseAbility->clone();
}
if (!a1)
return 0;
else
{
if(a1->target && !dynamic_cast<Player *>(a1->target))
a1->target = aTarget;
if(a1->oneShot)
{
a1->resolve();
SAFE_DELETE(a1);
}
else
a1->addToGame();
return 1;
}
return 0;
}
const string IfThenAbility::getMenuText()
{
return "";
}
IfThenAbility * IfThenAbility::clone() const
{
IfThenAbility * a = NEW IfThenAbility(*this);
a->delayedAbility = delayedAbility->clone();
return a;
}
IfThenAbility::~IfThenAbility()
{
SAFE_DELETE(delayedAbility);
SAFE_DELETE(delayedElseAbility);
}
//
//May Abilities
MayAbility::MayAbility(GameObserver* observer, int _id, MTGAbility * _ability, MTGCardInstance * _source, bool must,string _cond) :
MTGAbility(observer, _id, _source), NestedAbility(_ability), must(must), Cond(_cond)
{
triggered = 0;
mClone = NULL;
}
void MayAbility::Update(float dt)
{
MTGAbility::Update(dt);
if (!triggered && !game->getCurrentTargetChooser() && (!game->mLayers->actionLayer()->menuObject||game->mLayers->actionLayer()->menuObject == source))
{
triggered = 1;
if(Cond.size())
{
AbilityFactory af(game);
int checkCond = af.parseCastRestrictions(source,source->controller(),Cond);
if(!checkCond)
{
return;
}
}
if (TargetAbility * ta = dynamic_cast<TargetAbility *>(ability))
{
if (!ta->getActionTc()->validTargetsExist() || ta->getActionTc()->maxtargets == 0)
return;
}
game->mLayers->actionLayer()->setMenuObject(source, must);
previousInterrupter = game->isInterrupting;
game->mLayers->stackLayer()->setIsInterrupting(source->controller(), false);
}
}
const string MayAbility::getMenuText()
{
return ability->getMenuText();
}
int MayAbility::testDestroy()
{
if (!triggered)
return 0;
if (game->mLayers->actionLayer()->menuObject)
return 0;
if (game->mLayers->actionLayer()->getIndexOf(mClone) != -1)
return 0;
if(game->currentPlayer == source->controller() && game->isInterrupting == source->controller() && dynamic_cast<AManaProducer*>(AbilityFactory::getCoreAbility(ability)))
//if its my turn, and im interrupting myself(why?) then set interrupting to previous interrupter if the ability was a manaability
//special case since they don't use the stack.
game->mLayers->stackLayer()->setIsInterrupting(previousInterrupter, false);
return 1;
}
int MayAbility::isReactingToTargetClick(Targetable * card)
{
if (card == source)
{
if(Cond.size())
{
AbilityFactory af(game);
int checkCond = af.parseCastRestrictions(source,source->controller(),Cond);
if(!checkCond)
{
return 0;
}
}
return 1;
}
return 0;
}
int MayAbility::reactToTargetClick(Targetable * object)
{
mClone = ability->clone();
mClone->addToGame();
mClone->forceDestroy = 1;
return mClone->reactToTargetClick(object);
}
MayAbility * MayAbility::clone() const
{
MayAbility * a = NEW MayAbility(*this);
a->ability = ability->clone();
return a;
}
MayAbility::~MayAbility()
{
SAFE_DELETE(ability);
}
//Menu building ability Abilities
MenuAbility::MenuAbility(GameObserver* observer, int _id, Targetable * mtarget, MTGCardInstance * _source, bool must,vector<MTGAbility*>abilities,Player * who, string newName) :
MayAbility(observer, _id,NULL,_source,must), must(must),abilities(abilities),who(who),newNameString(newName)
{
triggered = 0;
mClone = NULL;
this->target = mtarget;
removeMenu = false;
vector<ManaCost*>optionalCost = vector<ManaCost*>();
toPay = NULL;
processed = false;
}
bool MenuAbility::CheckUserInput(JButton key)
{
if (game->mExtraPayment && key == JGE_BTN_SEC)
{
if(toPay && toPay->extraCosts == game->mExtraPayment)
{
//the user cancelled the paidability. fireAbility() on the second menu item.
//paidability will always occupy the abilities[0]; in the vector.
if(abilities.size() > 1)
{
abilities[1]->target = abilities[0]->target;
abilities[1]->fireAbility();
}
}
return false;
}
return false;
}
void MenuAbility::Update(float dt)
{
MTGAbility::Update(dt);
ActionLayer * object = game->mLayers->actionLayer();
if(toPay && game->mExtraPayment && !processed)
{
if(game->mExtraPayment->isPaymentSet() && game->mExtraPayment->canPay() )
{
if (game->mExtraPayment->costs.size())
{
if (game->mExtraPayment->costs[0]->costToPay)
{
ManaCost * diff = game->mExtraPayment->costs[0]->costToPay;
ManaCost * c = source->controller()->getManaPool()->Diff(diff);
source->X = c->getCost(Constants::NB_Colors);
delete c;
}
}
game->mExtraPayment->doPay();
game->mLayers->actionLayer()->reactToClick(game->mExtraPayment->action, game->mExtraPayment->source);
game->mExtraPayment = NULL;
processAbility();
return;
}
}
if (!triggered && !object->menuObject && !object->getCurrentTargetChooser())
{
triggered = 1;
object->currentActionCard = (MTGCardInstance*)this->target;
if (TargetAbility * ta = dynamic_cast<TargetAbility *>(ability))
{
if (!ta->getActionTc()->validTargetsExist())
return;
}
}
if(object->currentActionCard && this->target != object->currentActionCard)
{
triggered = 0;
}
if(triggered && !game->mExtraPayment && !processed)
{
game->mLayers->actionLayer()->setCustomMenuObject(source, must,abilities,newNameString.size()?newNameString.c_str():"");
previousInterrupter = game->isInterrupting;
game->mLayers->stackLayer()->setIsInterrupting(source->controller(), false);
}
}
int MenuAbility::resolve()
{
this->triggered = 1;
MTGAbility * a = this;
return a->addToGame();
}
const string MenuAbility::getMenuText()
{
if((abilities.size() > 1 && must)||(abilities.size() > 2 && !must))
return "choose one";
return "Action";
}
int MenuAbility::testDestroy()
{
if (game->mExtraPayment)
return 0;
if (!removeMenu)
return 0;
if (game->mLayers->actionLayer()->menuObject)
return 0;
if (game->mLayers->actionLayer()->getIndexOf(mClone) != -1)
return 0;
return 1;
}
int MenuAbility::isReactingToTargetClick(Targetable * card){return 0/*MayAbility::isReactingToTargetClick(card)*/;}
int MenuAbility::reactToTargetClick(Targetable * object){return 1;}
int MenuAbility::processAbility()
{
if(!mClone)
return 0;
if(processed)
return 0;
if(abilities[0])
mClone->target = abilities[0]->target;
if(MayAbility * toCheck = dynamic_cast<MayAbility*>(mClone))
{
toCheck->must = true;
mClone->addToGame();
}
else
{
mClone->oneShot = true;
mClone->forceDestroy = 1;
mClone->canBeInterrupted = false;
mClone->resolve();
SAFE_DELETE(mClone);
if (source->controller() == game->isInterrupting)
game->mLayers->stackLayer()->cancelInterruptOffer(ActionStack::DONT_INTERRUPT, false);
}
processed = true;
this->forceDestroy = 1;
removeMenu = true;
return 1;
}
int MenuAbility::reactToChoiceClick(Targetable * object,int choice,int control)
{
ActionElement * currentAction = (ActionElement *) game->mLayers->actionLayer()->mObjects[control];
if(currentAction != (ActionElement*)this)
return 0;
if(!abilities.size()||!triggered)
return 0;
for(int i = 0;i < int(abilities.size());i++)
{
if(choice == i)
mClone = abilities[choice]->clone();
else if(!optionalCosts.size())
SAFE_DELETE(abilities[i]);
if (mClone && !toPay && optionalCosts.size() && i < int(optionalCosts.size()) && optionalCosts[i])//paidability only supports the first ability as paid for now.
{
toPay = NEW ManaCost();
if (optionalCosts[i]->extraCosts)
toPay->extraCosts = optionalCosts[i]->extraCosts->clone();
toPay->addExtraCost(NEW ExtraManaCost(NEW ManaCost(optionalCosts[i])));
toPay->setExtraCostsAction(this, source);
game->mExtraPayment = toPay->extraCosts;
return 0;
}
}
if(!mClone)
{
if (source->controller() == game->isInterrupting)
game->mLayers->stackLayer()->cancelInterruptOffer(ActionStack::DONT_INTERRUPT, false);
return 0;
}
mClone->target = abilities[choice]->target;
processAbility();
return reactToTargetClick(object);
}
MenuAbility * MenuAbility::clone() const
{
MenuAbility * a = NEW MenuAbility(*this);
a->canBeInterrupted = false;
if(abilities.size())
{
for(int i = 0;i < int(abilities.size());i++)
{
a->abilities.push_back(abilities[i]->clone());
a->optionalCosts.push_back(NEW ManaCost(optionalCosts[i]));
a->abilities[i]->target = abilities[i]->target;
}
}
else
a->ability = ability->clone();
return a;
}
MenuAbility::~MenuAbility()
{
if(abilities.size())
{
for(int i = 0;i < int(abilities.size());i++)
{
if(abilities[i])
{
AASetColorChosen * chooseA = dynamic_cast<AASetColorChosen *>(abilities[i]);
if(chooseA && chooseA->abilityAltered)
SAFE_DELETE(chooseA->abilityAltered);
SAFE_DELETE(abilities[i]);
}
}
}
else
SAFE_DELETE(ability);
SAFE_DELETE(toPay);
SAFE_DELETE(mClone);
if(optionalCosts.size())
for(int i = 0;i < int(optionalCosts.size());i++)
{
if(optionalCosts[i])
{
SAFE_DELETE(optionalCosts[i]);
}
}
}
///
//MultiAbility : triggers several actions for a cost
MultiAbility::MultiAbility(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost) :
ActivatedAbility(observer, _id, card, _cost, 0)
{
if (_target)
target = _target;
}
int MultiAbility::Add(MTGAbility * ability)
{
abilities.push_back(ability);
return 1;
}
int MultiAbility::resolve()
{
Targetable * Phaseactiontarget = NULL;
for (size_t i = 0; i < abilities.size(); ++i)
{
if (abilities[i] == NULL)
continue;
Targetable * backup = abilities[i]->target;
if (target && target != source && abilities[i]->target == abilities[i]->source)
{
abilities[i]->target = target;
Phaseactiontarget = target;
}
abilities[i]->resolve();
abilities[i]->target = backup;
if(Phaseactiontarget && dynamic_cast<APhaseActionGeneric *> (abilities[i]))
abilities[i]->target = Phaseactiontarget;
}
return 1;
}
int MultiAbility::addToGame()
{
for (size_t i = 0; i < abilities.size(); ++i)
{
if (abilities[i] == NULL)
continue;
MTGAbility * a = abilities[i]->clone();
a->target = target;
a->addToGame();
clones.push_back(a);
}
MTGAbility::addToGame();
return 1;
}
int MultiAbility::destroy()
{
for (size_t i = 0; i < clones.size(); ++i)
{
//I'd like to call game->removeObserver here instead of using forceDestroy, but I get a weird crash after that, need to investigate a bit
clones[i]->forceDestroy = 1;
}
clones.clear();
return ActivatedAbility::destroy();
}
const string MultiAbility::getMenuText()
{
if (abilities.size() && abilities[0])
return abilities[0]->getMenuText();
return "";
}
MultiAbility * MultiAbility::clone() const
{
MultiAbility * a = NEW MultiAbility(*this);
a->abilities.clear();
for (size_t i = 0; i < abilities.size(); ++i)
{
if(abilities[i])
a->abilities.push_back(abilities[i]->clone());
}
return a;
}
MultiAbility::~MultiAbility()
{
for (size_t i = 0; i < abilities.size(); ++i)
{
SAFE_DELETE(abilities[i]);
}
abilities.clear();
}
//Generic Target Ability
GenericTargetAbility::GenericTargetAbility(GameObserver* observer, string newName, string castRestriction, int _id, MTGCardInstance * _source, TargetChooser * _tc, MTGAbility * a,
ManaCost * _cost, string limit,MTGAbility * sideEffects,string usesBeforeSideEffects, int restrictions, MTGGameZone * dest,string _tcString) :
TargetAbility(observer, _id, _source, _tc, _cost, restrictions, castRestriction), limit(limit), activeZone(dest),newName(newName),sideEffects(sideEffects),usesBeforeSideEffects(usesBeforeSideEffects),tcString(_tcString)
{
ability = a;
MTGAbility * core = AbilityFactory::getCoreAbility(a);
if (dynamic_cast<AACopier *> (core))
tc->other = true; //http://code.google.com/p/wagic/issues/detail?id=209 (avoid inifinite loop)
counters = 0;
}
const string GenericTargetAbility::getMenuText()
{
if (!ability)
return "Error";
if (newName.size())
return newName.c_str();
//Special case for move
MTGAbility * core = AbilityFactory::getCoreAbility(ability);
if (AAMover * move = dynamic_cast<AAMover *>(core))
return (move->getMenuText(tc));
return ability->getMenuText();
}
int GenericTargetAbility::resolve()
{
counters++;
tc->done = false;
if(sideEffects && usesBeforeSideEffects.size())
{
WParsedInt * use = NEW WParsedInt(usesBeforeSideEffects.c_str(),NULL,source);
uses = use->getValue();
delete use;
if(counters == uses)
{
sa = sideEffects->clone();
sa->target = this->target;
sa->source = this->source;
if(sa->oneShot)
{
sa->fireAbility();
}
else
{
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), sa);
wrapper->addToGame();
}
}
}
return TargetAbility::resolve();
}
int GenericTargetAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
{
limitPerTurn = 0;
if(limit.size())
{
WParsedInt value(limit.c_str(),NULL,source);
limitPerTurn = value.getValue();
}
if (limitPerTurn && counters >= limitPerTurn)
return 0;
if(tcString.size() && !tc->targetListSet())
{
TargetChooser * current = this->getActionTc();
TargetChooserFactory tcf(game);
TargetChooser *refreshed = tcf.createTargetChooser(tcString, source, this);
refreshed->setTargetsTo(current->getTargetsFrom());
this->setActionTC(refreshed);
SAFE_DELETE(current);
}
return TargetAbility::isReactingToClick(card, mana);
}
void GenericTargetAbility::Update(float dt)
{
if (newPhase != currentPhase && newPhase == MTG_PHASE_AFTER_EOT)
{
counters = 0;
}
TargetAbility::Update(dt);
}
int GenericTargetAbility::testDestroy()
{
if (!activeZone)
return TargetAbility::testDestroy();
if (activeZone->hasCard(source))
return 0;
return 1;
}
GenericTargetAbility * GenericTargetAbility::clone() const
{
GenericTargetAbility * a = NEW GenericTargetAbility(*this);
a->ability = ability->clone();
return a;
}
GenericTargetAbility::~GenericTargetAbility()
{
SAFE_DELETE(ability);
SAFE_DELETE(sideEffects);
}
//Alter Cost
AAlterCost::AAlterCost(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, int amount, int type) :
MTGAbility(observer, id, source, target), amount(amount), type(type)
{
manaReducer = source;
}
int AAlterCost::addToGame()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(!_target || _target->hasType("land"))
{
this->forceDestroy = 1;
return MTGAbility::addToGame();
}
if (amount > 0)
{
if(!_target->getIncreasedManaCost()->getConvertedCost())
{
ManaCost * increased = NEW ManaCost();
_target->getIncreasedManaCost()->copy(increased);
delete increased;
}
_target->getIncreasedManaCost()->add(type,amount);
}
else
{
if(!_target->getReducedManaCost()->getConvertedCost())
{
ManaCost * reduced = NEW ManaCost();
_target->getReducedManaCost()->copy(reduced);
delete reduced;
}
_target->getReducedManaCost()->add(type,abs(amount));
}
return MTGAbility::addToGame();
}
int AAlterCost::destroy()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
if(!this->manaReducer->isInPlay(game))
{
if (amount > 0)
{
_target->getIncreasedManaCost()->remove(type,amount);
refreshCost(_target);//special case for 0 cost.
}
else
{
_target->getReducedManaCost()->remove(type,abs(amount));
refreshCost(_target);//special case for 0 cost.
}
return MTGAbility::testDestroy();
}
return 0;
}
int AAlterCost::testDestroy()
{
if(!this->manaReducer->isInPlay(game))
{
return MTGAbility::testDestroy();
}
return 0;
}
void AAlterCost::refreshCost(MTGCardInstance * card)
{
ManaCost * original = NEW ManaCost();
original->copy(card->model->data->getManaCost());
if(card->getIncreasedManaCost()->getConvertedCost())
original->add(card->getIncreasedManaCost());
if(card->getReducedManaCost()->getConvertedCost())
original->remove(card->getReducedManaCost());
card->getManaCost()->copy(original);
delete original;
return;
}
void AAlterCost::increaseTheCost(MTGCardInstance * card)
{
if(card->getIncreasedManaCost()->getConvertedCost())
{
for(int k = Constants::MTG_COLOR_ARTIFACT; k < Constants::NB_Colors;k++)
{
card->getManaCost()->add(k,card->getIncreasedManaCost()->getCost(k));
}
}
return;
}
void AAlterCost::decreaseTheCost(MTGCardInstance * card)
{
if(card->getReducedManaCost()->getConvertedCost())
{
for(int k = Constants::MTG_COLOR_ARTIFACT; k < Constants::NB_Colors;k++)
{
card->getManaCost()->remove(k,card->getReducedManaCost()->getCost(k));
}
}
return;
}
AAlterCost * AAlterCost::clone() const
{
return NEW AAlterCost(*this);
}
AAlterCost::~AAlterCost()
{
}
// ATransformer
ATransformer::ATransformer(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, string stypes, string sabilities,string newpower,bool newpowerfound,string newtoughness,bool newtoughnessfound,vector<string> newAbilitiesList,bool newAbilityFound,bool aForever, bool aUntilNext,string _menu) :
MTGAbility(observer, id, source, target),newpower(newpower),newpowerfound(newpowerfound),newtoughness(newtoughness),newtoughnessfound(newtoughnessfound),newAbilitiesList(newAbilitiesList),newAbilityFound(newAbilityFound),aForever(aForever),UYNT(aUntilNext),menutext(_menu)
{
if (target != source) {
target->storedSourceCard = source;
}
PopulateAbilityIndexVector(abilities, sabilities);
PopulateColorIndexVector(colors, sabilities);
if(sabilities.find("chosencolor") != string::npos)
{
colors.push_back(source->chooseacolor);
}
myCurrentTurn = 1000;
//this subkeyword adds a color without removing the existing colors.
addNewColors = (sabilities.find("newcolors") != string::npos);
remove = (stypes.find("removealltypes") != string::npos);
removeCreatureSubtypes = (stypes.find("removecreaturesubtypes") != string::npos);
removeTypes = (stypes.find("removetypes") != string::npos);
if (stypes.find("allsubtypes") != string::npos || stypes.find("removecreaturesubtypes") != string::npos)
{
const vector<string> values = MTGAllCards::getValuesById();
for (size_t i = 0; i <values.size(); ++i)
{
if (!MTGAllCards::isSubtypeOfType(i,Subtypes::TYPE_CREATURE))
continue;
types.push_back(i);
}
}
else
{
if(stypes.find("chosentype") != string::npos)
{
stypes = source->chooseasubtype;
}
PopulateSubtypesIndexVector(types, stypes);
}
menu = stypes;
}
int ATransformer::addToGame()
{
if(UYNT)
myCurrentTurn = game->turn;
MTGCardInstance * _target = NULL;
Interruptible * action = (Interruptible *) target;
if (action && action->type == ACTION_SPELL && action->state == NOT_RESOLVED)
{
Spell * spell = (Spell *) action;
_target = spell->source;
aForever = true;
//when targeting the stack, set the effect to forever, incase the person does not use it
//otherwise we will end up with a null pointer on the destroy.
}
else
{
_target = (MTGCardInstance *) target;
}
if (!_target)
{
DebugTrace("ALL_ABILITIES: Target not set in ATransformer::addToGame\n");
return 0;
}
while (_target->next)
_target = _target->next;
for (int j = 0; j < Constants::NB_Colors; j++)
{
if (_target->hasColor(j))
oldcolors.push_back(j);
}
for (size_t j = 0; j < _target->types.size(); ++j)
oldtypes.push_back( _target->types[j]);
list<int>::iterator it;
for (it = colors.begin(); it != colors.end(); it++)
{
if(!addNewColors)
_target->setColor(0, 1);
}
if (removeTypes)
{
//remove the main types from a card, ie: hidden enchantment cycle.
for (int i = 0; i < Subtypes::LAST_TYPE; ++ i)
_target->removeType(i,1);
}
else if (remove)
{
for (it = oldtypes.begin(); it != oldtypes.end(); it++)
{
_target->removeType(*it);
}
}
else
{
for (it = types.begin(); it != types.end(); it++)
{
if(removeCreatureSubtypes)
{
_target->removeType(*it);
}
else if(_target->hasSubtype(*it))
{
//we generally don't want to give a creature type creature again
//all it does is create a sloppy mess of the subtype line on alternative quads
//also creates instances where a card gained a type from an ability like this one
//then loses the type through another ability, when this effect is destroyed the creature regains
//the type, which is wrong.
dontremove.push_back(*it);
}
else
{
_target->addType(*it);
}
}
}
for (it = colors.begin(); it != colors.end(); it++)
{
_target->setColor(*it);
}
for (it = abilities.begin(); it != abilities.end(); it++)
{
_target->basicAbilities.set(*it);
_target->modifiedbAbi += 1;
}
if(newAbilityFound)
{
for (unsigned int k = 0 ; k < newAbilitiesList.size();k++)
{
AbilityFactory af(game);
MTGAbility * aNew = af.parseMagicLine(newAbilitiesList[k], 0, NULL, _target);
if(!aNew)
continue;
GenericTargetAbility * gta = dynamic_cast<GenericTargetAbility*> (aNew);
if (gta)
{
((GenericTargetAbility *)aNew)->source = _target;
((GenericTargetAbility *)aNew)->ability->source = _target;
}
GenericActivatedAbility * gaa = dynamic_cast<GenericActivatedAbility*> (aNew);
if (gaa)
{
((GenericActivatedAbility *)aNew)->source = _target;
((GenericActivatedAbility *)aNew)->ability->source = _target;
}
MultiAbility * abi = dynamic_cast<MultiAbility*>(aNew);
if (abi)
{
((MultiAbility *)aNew)->source = _target;
((MultiAbility *)aNew)->abilities[0]->source = _target;
}
aNew->target = _target;
aNew->source = (MTGCardInstance *) _target;
if(aNew->oneShot)
{
aNew->resolve();
delete aNew;
}
else
{
aNew->addToGame();
newAbilities[_target].push_back(aNew);
}
}
}
if(newpowerfound || newtoughnessfound)
_target->isSettingBase += 1;
if(newpowerfound )
{
WParsedInt * val = NEW WParsedInt(newpower,NULL, source);
if(_target->isSwitchedPT)
{
_target->switchPT(false);
_target->addbaseP(val->getValue());
_target->switchPT(true);
}
else
_target->addbaseP(val->getValue());
delete val;
}
if(newtoughnessfound )
{//we should consider the damage if there is, if you have a 5/5 creature with 1 damage,
//and you turn it into 1/1, the 1 damage is still there and the creature must die...
//the toughness is intact but what we see in the game is the life...
WParsedInt * val = NEW WParsedInt(newtoughness,NULL, source);
if(_target->isSwitchedPT)
{
_target->switchPT(false);
_target->addbaseT(val->getValue());
_target->switchPT(true);
}
else
_target->addbaseT(val->getValue());
delete val;
}
return MTGAbility::addToGame();
}
int ATransformer::reapplyCountersBonus(MTGCardInstance * rtarget,bool , bool toughnessapplied)
{
if(!rtarget->counters || !rtarget->counters->counters.size())
return 0;
Counter * c = rtarget->counters->counters[0];
int rNewPower = 0;
int rNewToughness = 0;
for (int t = 0; t < rtarget->counters->mCount; t++)
{
if (c)
{
for(int i = 0;i < c->nb;i++)
{
rNewPower += c->power;
rNewToughness += c->toughness;
}
}
c = rtarget->counters->getNext(c);
}
if(toughnessapplied)
return rNewToughness;
return rNewPower;
}
int ATransformer::testDestroy()
{
if(UYNT)
{
if(myCurrentTurn != 1000 && game->turn > myCurrentTurn && source->controller()->getId() == game->currentPlayer->getId())
{
return 1;
}
}
return MTGAbility::testDestroy();
}
int ATransformer::destroy()
{
if(aForever)
return 0;
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next;
list<int>::iterator it;
if (!remove)
{
for (it = types.begin(); it != types.end(); it++)
{
bool removing = true;
for(unsigned int k = 0;k < dontremove.size();k++)
{
if(dontremove[k] == *it)
removing = false;
}
if(removing)
_target->removeType(*it);
}
//iterators annoy me :/
}
for (it = colors.begin(); it != colors.end(); it++)
{
_target->removeColor(*it);
}
for (it = abilities.begin(); it != abilities.end(); it++)
{
_target->basicAbilities.reset(*it);
_target->modifiedbAbi -= 1;
}
for (it = oldcolors.begin(); it != oldcolors.end(); it++)
{
_target->setColor(*it);
}
if(newpowerfound || newtoughnessfound)
_target->isSettingBase -= 1;
if(newpowerfound )
{
_target->revertbaseP();
}
if(newtoughnessfound )
{
_target->revertbaseT();
}
if(newAbilityFound)
{
for (unsigned int i = 0;i < newAbilities[_target].size(); i++)
{
if(newAbilities[_target].at(i))
{
newAbilities[_target].at(i)->forceDestroy = 1;
newAbilities[_target].at(i)->removeFromGame();
}
}
if (newAbilities.find(_target) != newAbilities.end())
{
newAbilities.erase(_target);
}
}
if (remove || removeCreatureSubtypes)
{
for (it = oldtypes.begin(); it != oldtypes.end(); it++)
{
if(!_target->hasSubtype(*it))
_target->addType(*it);
}
}
////in the case that we removed or added types to a card, so that it retains its original name when the effect is removed.
//if(_target->model->data->name.size())//tokens don't have a model name.
// _target->setName(_target->model->data->name.c_str());
//edit: this ability shouldn't have to reset the name on a card becuase removing a subtype changes the name of a land.
//that should be handled in addType...not here.
//im sure commenting this out will reintroduce a bug somewhere but it needs to be handled correctly. furthermore, why does adding and removing a type touch the name of a card?
}
return 1;
}
const string ATransformer::getMenuText()
{
if(menutext.size())
return menutext.c_str();
string s = menu;
sprintf(menuText, "Becomes %s", s.c_str());
return menuText;
}
ATransformer * ATransformer::clone() const
{
return NEW ATransformer(*this);
}
ATransformer::~ATransformer()
{
}
//ATransformerInstant
ATransformerInstant::ATransformerInstant(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, string types, string abilities,string newpower,bool newpowerfound,string newtoughness,bool newtoughnessfound,vector<string>newAbilitiesList,bool newAbilityFound,bool aForever,bool aUntilNext,string _menu) :
InstantAbility(observer, id, source, target),newpower(newpower),newpowerfound(newpowerfound),newtoughness(newtoughness),newtoughnessfound(newtoughnessfound),newAbilitiesList(newAbilitiesList),newAbilityFound(newAbilityFound),aForever(aForever),UYNT(aUntilNext),menu(_menu)
{
ability = NEW ATransformer(game, id, source, target, types, abilities,newpower,newpowerfound,newtoughness,newtoughnessfound,newAbilitiesList,newAbilityFound,aForever,aUntilNext,_menu);
aType = MTGAbility::STANDARD_BECOMES;
}
int ATransformerInstant::resolve()
{
ATransformer * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
const string ATransformerInstant::getMenuText()
{
if(menu.size())
return menu.c_str();
return ability->getMenuText();
}
ATransformerInstant * ATransformerInstant::clone() const
{
ATransformerInstant * a = NEW ATransformerInstant(*this);
a->ability = this->ability->clone();
return a;
}
ATransformerInstant::~ATransformerInstant()
{
SAFE_DELETE(ability);
}
//P/t ueot
PTInstant::PTInstant(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, WParsedPT * wppt,string s,bool nonstatic) :
InstantAbility(observer, id, source, target), wppt(wppt),s(s),nonstatic(nonstatic)
{
ability = NEW APowerToughnessModifier(game, id, source, target, wppt,s,nonstatic);
aType = MTGAbility::STANDARD_PUMP;
}
int PTInstant::resolve()
{
APowerToughnessModifier * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
((Damageable *) (this->target))->afterDamage();//additional check the negative pt after resolving..
return 1;
}
const string PTInstant::getMenuText()
{
return ability->getMenuText();
}
PTInstant * PTInstant::clone() const
{
PTInstant * a = NEW PTInstant(*this);
a->ability = this->ability->clone();
return a;
}
PTInstant::~PTInstant()
{
SAFE_DELETE(ability);
}
// ASwapPTUEOT
ASwapPTUEOT::ASwapPTUEOT(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target) :
InstantAbility(observer, id, source, target)
{
ability = NEW ASwapPT(observer, id, source, target);
}
int ASwapPTUEOT::resolve()
{
ASwapPT * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
const string ASwapPTUEOT::getMenuText()
{
return ability->getMenuText();
}
ASwapPTUEOT * ASwapPTUEOT::clone() const
{
ASwapPTUEOT * a = NEW ASwapPTUEOT(*this);
a->ability = this->ability->clone();
return a;
}
ASwapPTUEOT::~ASwapPTUEOT()
{
SAFE_DELETE(ability);
}
//exhange life with targetchooser
AAExchangeLife::AAExchangeLife(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, ManaCost * _cost,
int who) :
ActivatedAbilityTP(observer, _id, _source, _target, _cost, who)
{
}
int AAExchangeLife::resolve()
{
Damageable * _target = (Damageable *) getTarget();
if (_target)
{
if(_target->type_as_damageable == Damageable::DAMAGEABLE_PLAYER && ((Player*)_target)->inPlay()->hasAbility(Constants::CANTCHANGELIFE))
return 0;
Player *player = source->controller();
int oldlife = player->getLife();
int targetOldLife = _target->getLife();
int modifier = oldlife > targetOldLife? oldlife - targetOldLife:targetOldLife - oldlife;
if (_target->type_as_damageable == Damageable::DAMAGEABLE_MTGCARDINSTANCE)
{
int increaser = 0;
MTGCardInstance * card = ((MTGCardInstance*)_target);
int toughMod = 0;
targetOldLife <= card->origtoughness?toughMod = card->origtoughness - targetOldLife: toughMod = targetOldLife - card->origtoughness;
if(oldlife > targetOldLife)
{
increaser = oldlife - targetOldLife;
player->gainOrLoseLife(modifier * -1);
card->addToToughness(increaser+toughMod);
}
else
{
_target->life = oldlife;
card->toughness = oldlife;
player->gainOrLoseLife(modifier);
}
return 1;
}
Player * opponent = (Player*)_target;
if(oldlife > targetOldLife)
{
player->gainOrLoseLife(modifier * -1);
opponent->gainOrLoseLife(modifier);
}
else
{
player->gainOrLoseLife(modifier);
opponent->gainOrLoseLife(modifier * -1);
}
return 1;
}
return 0;
}
const string AAExchangeLife::getMenuText()
{
return "Exchange life";
}
AAExchangeLife * AAExchangeLife::clone() const
{
return NEW AAExchangeLife(*this);
}
//ALoseAbilities
ALoseAbilities::ALoseAbilities(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target) :
MTGAbility(observer, id, source)
{
target = _target;
}
int ALoseAbilities::addToGame()
{
if (storedAbilities.size())
{
DebugTrace("FATAL:storedAbilities shouldn't be already set inALoseAbilitie\n");
return 0;
}
MTGCardInstance * _target = (MTGCardInstance *)target;
ActionLayer * al = game->mLayers->actionLayer();
//Build a list of Lords in game, this is a hack mostly for lands, see below
vector <ALord *> lordsInGame;
for (int i = (int)(al->mObjects.size()) - 1; i > 0; i--) //0 is not a mtgability...hackish
{
if (al->mObjects[i])
{
MTGAbility * currentAction = (MTGAbility *) al->mObjects[i];
ALord * l = dynamic_cast<ALord*> (currentAction);
if(l)
lordsInGame.push_back(l);
}
}
for (int i = (int)(al->mObjects.size()) - 1; i > 0; i--) //0 is not a mtgability...hackish
{
if (al->mObjects[i])
{
MTGAbility * currentAction = (MTGAbility *) al->mObjects[i];
ALoseAbilities * la = dynamic_cast<ALoseAbilities*> (currentAction);
if(la)
continue;
if (currentAction->source == _target)
{
bool canRemove = true;
//Hack: we don't remove abilities on the card if they are provided by an external lord ability.
//This is partly to solve the following issues:
// http://code.google.com/p/wagic/issues/detail?id=647
// http://code.google.com/p/wagic/issues/detail?id=700
// But also because "most" abilities granted by lords will actually go away by themselves,
// based on the fact that we usually remove abilities AND change the type of the card
//Also in a general way we don't want to remove the card's abilities if it is provided by a Lord,
//although there is also a problem with us not handling the P/T layer correctly
for (size_t i = 0; i < lordsInGame.size(); ++i)
{
if (lordsInGame[i]->isParentOf(_target, currentAction))
{
canRemove = false;
break;
}
}
if (canRemove)
{
storedAbilities.push_back(currentAction);
al->removeFromGame(currentAction);
}
}
}
}
return MTGAbility::addToGame();
}
int ALoseAbilities::destroy()
{
for (size_t i = 0; i < storedAbilities.size(); ++i)
{
MTGAbility * a = storedAbilities[i];
//OneShot abilities are not supposed to stay in the game for long.
// If we copied one, something wrong probably happened
if (a->oneShot)
{
DebugTrace("ALLABILITIES: Ability should not be one shot");
continue;
}
//Avoid inifinite loop of removing/putting back abilities
if (dynamic_cast<ALoseAbilities*> (a))
{
DebugTrace("ALLABILITIES: loseability won't be put in the loseability list");
continue;
}
a->addToGame();
}
storedAbilities.clear();
return 1;
}
ALoseAbilities * ALoseAbilities::clone() const
{
return NEW ALoseAbilities(*this);
}
//ALoseSubtypes
ALoseSubtypes::ALoseSubtypes(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target, int parentType) :
MTGAbility(observer, id, source), parentType(parentType)
{
target = _target;
}
int ALoseSubtypes::addToGame()
{
if (storedSubtypes.size())
{
DebugTrace("FATAL:storedSubtypes shouldn't be already set inALoseSubtypes\n");
return 0;
}
MTGCardInstance * _target = (MTGCardInstance *)target;
for (int i = ((int)_target->types.size())-1; i >= 0; --i)
{
int subtype = _target->types[i];
if (MTGAllCards::isSubtypeOfType(subtype, parentType))
{
storedSubtypes.push_back(subtype);
_target->removeType(subtype);
}
}
return MTGAbility::addToGame();
}
int ALoseSubtypes::destroy()
{
MTGCardInstance * _target = (MTGCardInstance *)target;
for (size_t i = 0; i < storedSubtypes.size(); ++i)
_target->addType(storedSubtypes[i]);
storedSubtypes.clear();
return 1;
}
ALoseSubtypes * ALoseSubtypes::clone() const
{
return NEW ALoseSubtypes(*this);
}
//APreventDamageTypes
APreventDamageTypes::APreventDamageTypes(GameObserver* observer, int id, MTGCardInstance * source, string to, string from, int type) :
MTGAbility(observer, id, source), to(to), from(from), type(type)
{
re = NULL;
}
int APreventDamageTypes::addToGame()
{
if (re)
{
DebugTrace("FATAL:re shouldn't be already set in APreventDamageTypes\n");
return 0;
}
TargetChooserFactory tcf(game);
TargetChooser *toTc = tcf.createTargetChooser(to, source, this);
if (toTc)
toTc->targetter = NULL;
TargetChooser *fromTc = tcf.createTargetChooser(from, source, this);
if (fromTc)
fromTc->targetter = NULL;
if (type != 1 && type != 2)
{//not adding this creates a memory leak.
re = NEW REDamagePrevention(this, fromTc, toTc, -1, false, Damage::DAMAGE_COMBAT);
}
else if (type == 1)
{
re = NEW REDamagePrevention(this, fromTc, toTc, -1, false, Damage::DAMAGE_ALL_TYPES);
}
else if (type == 2)
{
re = NEW REDamagePrevention(this, fromTc, toTc, -1, false, Damage::DAMAGE_OTHER);
}
game->replacementEffects->add(re);
return MTGAbility::addToGame();
}
int APreventDamageTypes::destroy()
{
game->replacementEffects->remove(re);
SAFE_DELETE(re);
return 1;
}
APreventDamageTypes * APreventDamageTypes::clone() const
{
APreventDamageTypes * a = NEW APreventDamageTypes(*this);
a->re = NULL;
return a;
}
APreventDamageTypes::~APreventDamageTypes()
{
SAFE_DELETE(re);
}
//APreventDamageTypesUEOT
APreventDamageTypesUEOT::APreventDamageTypesUEOT(GameObserver* observer, int id, MTGCardInstance * source, string to, string from, int type) :
InstantAbility(observer, id, source)
{
ability = NEW APreventDamageTypes(observer, id, source, to, from, type);
}
int APreventDamageTypesUEOT::resolve()
{
APreventDamageTypes * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source, (Damageable *) (this->target), a);
wrapper->addToGame();
return 1;
}
int APreventDamageTypesUEOT::destroy()
{
for (size_t i = 0; i < clones.size(); ++i)
{
clones[i]->forceDestroy = 0;
}
clones.clear();
return 1;
}
const string APreventDamageTypesUEOT::getMenuText()
{
return ability->getMenuText();
}
APreventDamageTypesUEOT * APreventDamageTypesUEOT::clone() const
{
APreventDamageTypesUEOT * a = NEW APreventDamageTypesUEOT(*this);
a->ability = this->ability->clone();
return a;
}
APreventDamageTypesUEOT::~APreventDamageTypesUEOT()
{
SAFE_DELETE(ability);
}
//AVanishing creature also fading
// Comprehensive 702.31a:
// Fading is a keyword that represents two abilities.
// “Fading N” means “This permanent comes into play with N fade counters on it” and
// “At the beginning of your upkeep, remove a fade counter from this permanent.
// If you cant, sacrifice the permanent.”
// Comprehensive 702.62a:
// Vanishing is a keyword that represents three abilities.
// "Vanishing N" means "This permanent comes into play with N time counters on it,"
// "At the beginning of your upkeep, if this permanent has a time counter on it, remove a time counter from it," and
// "When the last time counter is removed from this permanent, sacrifice it."
AVanishing::AVanishing(GameObserver* observer, int _id, MTGCardInstance * card, ManaCost *, int, int amount, string counterName) :
MTGAbility(observer, _id, source, target),amount(amount),counterName(counterName)
{
target = card;
source = card;
for (int i = 0; i < amount; i++)
source->counters->addCounter(counterName.c_str(), 0, 0);
}
void AVanishing::Update(float dt)
{
if (newPhase != currentPhase && source->controller() == game->currentPlayer)
{
if (newPhase == MTG_PHASE_UPKEEP)
{
bool hasCounters = (source->counters && source->counters->hasCounter(counterName.c_str(), 0, 0));
if (hasCounters)
{
// sacrifice of card with time counter is handled in removeCounter
source->counters->removeCounter(counterName.c_str(), 0, 0);
} else
{
if (counterName == "fade")
{
MTGCardInstance * beforeCard = source;
source->controller()->game->putInGraveyard(source);
WEvent * e = NEW WEventCardSacrifice(beforeCard,source);
game->receiveEvent(e);
}
}
}
}
MTGAbility::Update(dt);
}
int AVanishing::resolve()
{
return 1;
}
const string AVanishing::getMenuText()
{
if (counterName.find("fade") != string::npos)
{
return "Fading";
}
return "Vanishing";
}
AVanishing * AVanishing::clone() const
{
return NEW AVanishing(*this);
}
AVanishing::~AVanishing()
{
}
//Produce Mana
AProduceMana::AProduceMana(GameObserver* observer, int _id, MTGCardInstance * _source, string ManaDescription) :
MTGAbility(observer, _id, source),ManaDescription(ManaDescription)
{
source = _source;
mana[0] = "{g}"; mana[1] = "{u}"; mana[2] = "{r}"; mana[3] = "{b}"; mana[4] = "{w}";
}
int AProduceMana::receiveEvent(WEvent * event)
{
if(WEventCardTappedForMana * isTappedForMana = dynamic_cast<WEventCardTappedForMana *> (event))
{
if ((isTappedForMana->card == source)||(isTappedForMana->card == source->target && ManaDescription == "selectmana"))
produce();
}
return 1;
}
int AProduceMana::produce()
{
if(ManaDescription == "selectmana")
{//I tried menu ability and vector<MTGAbility*abi> to have a shorter code but it crashes wagic at end of turn...
//The may ability on otherhand works but the ability is cumulative...
//This must be wrapped on menuability so we can use it on successions...
AManaProducer *ap0 = NEW AManaProducer(game, game->mLayers->actionLayer()->getMaxId(), source, source->controller(), ManaCost::parseManaCost(mana[0],NULL,source), NULL, 0,"",false);
MayAbility *mw0 = NEW MayAbility(game, game->mLayers->actionLayer()->getMaxId(), ap0, source,true);
MTGAbility *ga0 = NEW GenericAddToGame(game, game->mLayers->actionLayer()->getMaxId(), source,NULL,mw0);
AManaProducer *ap1 = NEW AManaProducer(game, game->mLayers->actionLayer()->getMaxId(), source, source->controller(), ManaCost::parseManaCost(mana[1],NULL,source), NULL, 0,"",false);
MayAbility *mw1 = NEW MayAbility(game, game->mLayers->actionLayer()->getMaxId(), ap1, source,true);
MTGAbility *ga1 = NEW GenericAddToGame(game, game->mLayers->actionLayer()->getMaxId(), source,NULL,mw1);
AManaProducer *ap2 = NEW AManaProducer(game, game->mLayers->actionLayer()->getMaxId(), source, source->controller(), ManaCost::parseManaCost(mana[2],NULL,source), NULL, 0,"",false);
MayAbility *mw2 = NEW MayAbility(game, game->mLayers->actionLayer()->getMaxId(), ap2, source,true);
MTGAbility *ga2 = NEW GenericAddToGame(game, game->mLayers->actionLayer()->getMaxId(), source,NULL,mw2);
AManaProducer *ap3 = NEW AManaProducer(game, game->mLayers->actionLayer()->getMaxId(), source, source->controller(), ManaCost::parseManaCost(mana[3],NULL,source), NULL, 0,"",false);
MayAbility *mw3 = NEW MayAbility(game, game->mLayers->actionLayer()->getMaxId(), ap3, source,true);
MTGAbility *ga3 = NEW GenericAddToGame(game, game->mLayers->actionLayer()->getMaxId(), source,NULL,mw3);
AManaProducer *ap4 = NEW AManaProducer(game, game->mLayers->actionLayer()->getMaxId(), source, source->controller(), ManaCost::parseManaCost(mana[4],NULL,source), NULL, 0,"",false);
MayAbility *mw4 = NEW MayAbility(game, game->mLayers->actionLayer()->getMaxId(), ap4, source,true);
MTGAbility *ga4 = NEW GenericAddToGame(game, game->mLayers->actionLayer()->getMaxId(), source,NULL,mw4);
ga0->resolve();
ga1->resolve();
ga2->resolve();
ga3->resolve();
ga4->resolve();
}
else
{
AManaProducer *amp = NEW AManaProducer(game, game->mLayers->actionLayer()->getMaxId(), source, source->controller(), ManaCost::parseManaCost(ManaDescription,NULL,source), NULL, 0,"",false);
amp->resolve();
SAFE_DELETE(amp);//once you call resolve() on a ability, you can safely delete it.
}
return 1;
}
const string AProduceMana::getMenuText()
{
return "Produce Mana";
}
AProduceMana * AProduceMana::clone() const
{
return NEW AProduceMana(*this);
}
AProduceMana::~AProduceMana()
{
}
//AUpkeep
AUpkeep::AUpkeep(GameObserver* observer, int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int restrictions, int _phase,
int _once,bool Cumulative) :
ActivatedAbility(observer, _id, card, _cost, restrictions), NestedAbility(a), phase(_phase), once(_once),Cumulative(Cumulative)
{
paidThisTurn = 0;
aType = MTGAbility::UPCOST;
if(Cumulative)
{
backupMana = NEW ManaCost();
backupMana->copy(this->getCost());
backupMana->addExtraCosts(this->getCost()->extraCosts);
}
}
int AUpkeep::receiveEvent(WEvent * event)
{
if (WEventPhaseChange* pe = dynamic_cast<WEventPhaseChange*>(event))
{
if (MTG_PHASE_DRAW == pe->to->id && MTG_PHASE_UPKEEP == pe->from->id)
{
if (source->controller() == game->currentPlayer && once < 2 && paidThisTurn < 1)
{
ability->resolve();
}
}
}
return 1;
}
void AUpkeep::Update(float dt)
{
// once: 0 means always go off, 1 means go off only once, 2 means go off only once and already has.
if (newPhase != currentPhase && source->controller() == game->currentPlayer && once < 2)
{
if (newPhase == MTG_PHASE_BEFORE_BEGIN)
{
paidThisTurn = 0;
}
else if(newPhase == MTG_PHASE_UPKEEP && Cumulative )
{
source->counters->addCounter("age",0,0);
Counter * targetCounter = NULL;
currentage = 0;
if (source->counters && source->counters->hasCounter("age", 0, 0))
{
targetCounter = source->counters->hasCounter("age", 0, 0);
currentage = targetCounter->nb - 1;
}
if(currentage)
{
paidThisTurn = 0;
this->getCost()->copy(backupMana);
for(int age = 0;age < currentage;age++)
{
this->getCost()->add(backupMana);
this->getCost()->addExtraCosts(backupMana->extraCosts);
}
}
}
if (newPhase == phase + 1 && once)
once = 2;
}
ActivatedAbility::Update(dt);
}
int AUpkeep::isReactingToClick(MTGCardInstance * card, ManaCost * mana)
{
if (currentPhase != phase || paidThisTurn > 0 || once >= 2)
return 0;
return ActivatedAbility::isReactingToClick(card, mana);
}
int AUpkeep::resolve()
{
paidThisTurn += 1;
return 1;
}
const string AUpkeep::getMenuText()
{
return "Upkeep";
}
ostream& AUpkeep::toString(ostream& out) const
{
out << "AUpkeep ::: paidThisTurn : " << paidThisTurn << " (";
return ActivatedAbility::toString(out) << ")";
}
AUpkeep * AUpkeep::clone() const
{
AUpkeep * a = NEW AUpkeep(*this);
a->ability = ability->clone();
return a;
}
AUpkeep::~AUpkeep()
{
if(Cumulative)
{
SAFE_DELETE(backupMana);
}
SAFE_DELETE(ability);
}
//A Phase based Action
APhaseAction::APhaseAction(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance *, string sAbility, int, int _phase,bool forcedestroy,bool next,bool myturn,bool opponentturn,bool once, bool checkexile) :
MTGAbility(observer, _id, card),sAbility(sAbility), phase(_phase),forcedestroy(forcedestroy),next(next),myturn(myturn),opponentturn(opponentturn),once(once),checkexile(checkexile)
{
abilityId = _id;
abilityOwner = card->controller();
psMenuText = "";
AbilityFactory af(game);
ability = af.parseMagicLine(sAbility, abilityId, NULL, NULL);
if(ability)
psMenuText = ability->getMenuText();
else
psMenuText = sAbility.c_str();
delete (ability);
}
void APhaseAction::Update(float dt)
{
if(checkexile)
{
if(((MTGCardInstance *)target)->next->getCurrentZone() != ((MTGCardInstance *)target)->owner->game->exile)
{
this->forceDestroy = 1;
return;
}
}
if (newPhase != currentPhase)
{
if((myturn && game->currentPlayer == source->controller())||
(opponentturn && game->currentPlayer != source->controller())/*||*/
/*(myturn && opponentturn)*/)
{
if(newPhase == phase && next )
{
MTGCardInstance * _target = NULL;
bool isTargetable = false;
if(target)
{
_target = static_cast<MTGCardInstance *>(target);
isTargetable = (_target && !_target->currentZone && _target != this->source);
}
if(!sAbility.size() || (!target || isTargetable))
{
this->forceDestroy = 1;
return;
}
else
{
while(_target && _target->next)
_target = _target->next;
}
AbilityFactory af(game);
MTGAbility * ability = af.parseMagicLine(sAbility, abilityId, NULL, _target);
MTGAbility * a = ability->clone();
a->target = _target;
a->resolve();
delete (a);
delete (ability);
if(this->oneShot || once)
{
this->forceDestroy = 1;
}
}
else if(newPhase == phase && next == false)
next = true;
}
}
MTGAbility::Update(dt);
}
int APhaseAction::resolve()
{
return 0;
}
const string APhaseAction::getMenuText()
{
if(psMenuText.size())
return psMenuText.c_str();
else
return "Phase Based Action";
}
APhaseAction * APhaseAction::clone() const
{
APhaseAction * a = NEW APhaseAction(*this);
if(forcedestroy == false)
a->forceDestroy = -1;// we want this ability to stay alive until it resolves.
return a;
}
APhaseAction::~APhaseAction()
{
}
// the main ability
APhaseActionGeneric::APhaseActionGeneric(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * target, string sAbility, int restrictions, int _phase,bool forcedestroy,bool next,bool myturn,bool opponentturn,bool once, bool checkexile) :
InstantAbility(observer, _id, card, target)
{
MTGCardInstance * _target = target;
ability = NEW APhaseAction(game, _id, card,_target, sAbility, restrictions, _phase,forcedestroy,next,myturn,opponentturn,once,checkexile);
}
int APhaseActionGeneric::resolve()
{
APhaseAction * a = ability->clone();
a->target = target;
a->addToGame();
return 1;
}
const string APhaseActionGeneric::getMenuText()
{
return ability->getMenuText();
}
APhaseActionGeneric * APhaseActionGeneric::clone() const
{
APhaseActionGeneric * a = NEW APhaseActionGeneric(*this);
a->ability = this->ability->clone();
a->oneShot = 1;
return a;
}
APhaseActionGeneric::~APhaseActionGeneric()
{
SAFE_DELETE(ability);
}
//AAttackSetCost
AAttackSetCost::AAttackSetCost(GameObserver* observer, int _id, MTGCardInstance * _source, string number, bool pw) :
MTGAbility(observer, _id, _source), number(number), pw(pw)
{
}
void AAttackSetCost::Update(float dt)
{
if(game->getCurrentGamePhase() != MTG_PHASE_COMBATATTACKERS)
{
source->attackCost = source->attackCostBackup;
if(pw)
source->attackPlaneswalkerCost = source->attackPlaneswalkerCostBackup;
MTGAbility::Update(dt);
}
}
int AAttackSetCost::addToGame()
{
WParsedInt attackcost(number, NULL, source);
source->attackCost += attackcost.getValue();
source->attackCostBackup += attackcost.getValue();
if(pw)
{
source->attackPlaneswalkerCost += attackcost.getValue();
source->attackPlaneswalkerCostBackup += attackcost.getValue();
}
return MTGAbility::addToGame();
}
int AAttackSetCost::destroy()
{
WParsedInt attackcost(number, NULL, source);
source->attackCost -= attackcost.getValue();
source->attackCostBackup -= attackcost.getValue();
if(pw)
{
source->attackPlaneswalkerCost -= attackcost.getValue();
source->attackPlaneswalkerCostBackup -= attackcost.getValue();
}
return 1;
}
const string AAttackSetCost::getMenuText()
{
return "Attack Cost";
}
AAttackSetCost * AAttackSetCost::clone() const
{
return NEW AAttackSetCost(*this);
}
//ABlockSetCost
ABlockSetCost::ABlockSetCost(GameObserver* observer, int _id, MTGCardInstance * _source, string number) :
MTGAbility(observer, _id, _source), number(number)
{
}
void ABlockSetCost::Update(float dt)
{
if(game->getCurrentGamePhase() != MTG_PHASE_COMBATBLOCKERS)
{
source->blockCost = source->blockCostBackup;
MTGAbility::Update(dt);
}
}
int ABlockSetCost::addToGame()
{
WParsedInt blockCost(number, NULL, source);
source->blockCost += blockCost.getValue();
source->blockCostBackup += blockCost.getValue();
return MTGAbility::addToGame();
}
int ABlockSetCost::destroy()
{
WParsedInt blockCost(number, NULL, source);
source->blockCost -= blockCost.getValue();
source->blockCostBackup -= blockCost.getValue();
return 1;
}
const string ABlockSetCost::getMenuText()
{
return "Block Cost";
}
ABlockSetCost * ABlockSetCost::clone() const
{
return NEW ABlockSetCost(*this);
}
//AShackle
AShackle::AShackle(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target) :
MTGAbility(observer, _id, card)
{
target = _target;
Shackled = NULL;
previousController = NULL;
resolved = false;
}
void AShackle::Update(float dt)
{
if (resolved == false)
{
resolved = true;
resolveShackle();
}
if (!source->isTapped() || !source->isInPlay(game))
{
if (Shackled == NULL || !Shackled->isInPlay(game))
MTGAbility::Update(dt);
MTGCardInstance * _target = Shackled;
returntoOwner(_target);
}
MTGAbility::Update(dt);
}
void AShackle::resolveShackle()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
previousController = _target->controller();
previousController->game->putInZone(_target, _target->currentZone,
source->controller()->game->inPlay);
Shackled = _target;
source->shackled = Shackled;
Shackled->shackled = source;
}
}
void AShackle::returntoOwner(MTGCardInstance* _target) {
MTGCardInstance * cardToReturn = _target;
if(!cardToReturn)
{
if (source)
source->shackled = NULL;
this->forceDestroy = 1;
return;
}
if(previousController && cardToReturn->isInPlay(game))
{
cardToReturn->shackled = NULL;
cardToReturn->controller()->game->putInZone(_target, _target->currentZone,
previousController->game->inPlay);
}
if (source)
source->shackled = NULL;
this->forceDestroy = 1;
Shackled = NULL;
return;
}
int AShackle::resolve()
{
return 0;
}
const string AShackle::getMenuText()
{
return "Gain Control";
}
AShackle * AShackle::clone() const
{
AShackle * a = NEW AShackle(*this);
a->forceDestroy = -1;
return a;
};
AShackle::~AShackle()
{
}
AShackleWrapper::AShackleWrapper(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target) :
InstantAbility(observer, _id, source, _target)
{
ability = NEW AShackle(observer, _id,card,_target);
}
int AShackleWrapper::resolve()
{
AShackle * a = ability->clone();
a->target = target;
a->addToGame();
return 1;
}
const string AShackleWrapper::getMenuText()
{
return "Gain Control";
}
AShackleWrapper * AShackleWrapper::clone() const
{
AShackleWrapper * a = NEW AShackleWrapper(*this);
a->ability = this->ability->clone();
a->oneShot = 1;
return a;
}
AShackleWrapper::~AShackleWrapper()
{
SAFE_DELETE(ability);
}
//grant
AGrant::AGrant(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, MTGAbility * _Grant) :
MTGAbility(observer, _id, card)
{
Granted = _Grant;
target = _target;
Blessed = NULL;
resolved = false;
toGrant = NULL;
}
void AGrant::Update(float dt)
{
if (resolved == false)
{
resolved = true;
resolveGrant();
}
if (!source->isTapped() || !source->isInPlay(game))
{
if (Blessed == NULL || !Blessed->isInPlay(game))
MTGAbility::Update(dt);
MTGCardInstance * _target = Blessed;
removeGranted(_target);
}
else
resolveGrant();
MTGAbility::Update(dt);
}
void AGrant::resolveGrant()
{
if (toGrant) return;
MTGCardInstance * _target = (MTGCardInstance *)target;
if (_target)
{
toGrant = Granted->clone();
toGrant->target = _target;
toGrant->addToGame();
Blessed = _target;
}
}
void AGrant::removeGranted(MTGCardInstance* _target)
{
if (!toGrant) return;
game->removeObserver(toGrant);
game->removeObserver(this);
Blessed = NULL;
return;
}
int AGrant::resolve()
{
return 0;
}
const string AGrant::getMenuText()
{
return Granted->getMenuText();
}
AGrant * AGrant::clone() const
{
AGrant * a = NEW AGrant(*this);
a->forceDestroy = -1;
a->Granted = Granted->clone();
return a;
};
AGrant::~AGrant()
{
SAFE_DELETE(Granted);
}
AGrantWrapper::AGrantWrapper(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, MTGAbility * _Grant) :
InstantAbility(observer, _id, source, _target), Granted(_Grant)
{
ability = NEW AGrant(observer, _id, card, _target,_Grant);
}
int AGrantWrapper::resolve()
{
AGrant * a = ability->clone();
a->target = target;
a->addToGame();
return 1;
}
const string AGrantWrapper::getMenuText()
{
return "Grant";
}
AGrantWrapper * AGrantWrapper::clone() const
{
AGrantWrapper * a = NEW AGrantWrapper(*this);
a->ability = this->ability->clone();
a->oneShot = 1;
return a;
}
AGrantWrapper::~AGrantWrapper()
{
SAFE_DELETE(ability);
}
//a blink
ABlink::ABlink(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, bool blinkueot, bool blinkForSource, bool blinkhand, MTGAbility * stored) :
MTGAbility(observer, _id, card),blinkueot(blinkueot),blinkForSource(blinkForSource),blinkhand(blinkhand),stored(stored)
{
target = _target;
Blinked = NULL;
resolved = false;
}
void ABlink::Update(float dt)
{
if (resolved == false)
{
resolved = true;
resolveBlink();
}
if ((blinkueot && currentPhase == MTG_PHASE_ENDOFTURN) || (blinkForSource && !source->isInPlay(game)))
{
if(Blinked->blinked)
{
if (Blinked == NULL)
MTGAbility::Update(dt);
MTGCardInstance * _target = Blinked;
returnCardIntoPlay(_target);
}
}
MTGAbility::Update(dt);
}
void ABlink::resolveBlink()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
//going to comment this condiational out, i can't remember if i added this to fix some kind of bug
//which is why i plan on leaving it here. seems to work fine though without it.
//if(blinkhand && !_target->controller()->game->isInZone(_target,_target->controller()->game->hand))
//{
// this->forceDestroy = 1;
// return;
//}
//else if(!blinkhand && !_target->controller()->game->isInZone(_target,_target->controller()->game->battlefield))
//{
// this->forceDestroy = 1;
// return;
//}
if (_target->MeldedFrom.size())
{
//cards with meld are handled very different from normal cards with this specific ability giving us about 3 of the
//core rules for the ability. below we split the card up, and we send them to garbage, move the original to temp where
//it is later moved to garbage by garbage collection.
//then we build 2 seperate blinks with the 2 parts as the targets.
vector<string> names = split(_target->MeldedFrom, '|');
MTGCard * cardone = MTGCollection()->getCardByName(names[0]);
MTGCardInstance * cardOne = NEW MTGCardInstance(cardone, _target->owner->game);
MTGCard * cardtwo = MTGCollection()->getCardByName(names[1]);
MTGCardInstance * cardTwo = NEW MTGCardInstance(cardtwo, _target->owner->game);
_target->controller()->game->putInZone(_target, _target->currentZone,
_target->owner->game->temp);
_target->controller()->game->garbage->addCard(cardOne);
_target->controller()->game->garbage->addCard(cardTwo);
MTGAbility * a = NEW ABlinkGeneric(game, game->mLayers->actionLayer()->getMaxId(), source, cardOne, blinkueot, blinkForSource, blinkhand, stored);
a->target = (Targetable*)cardOne;
a->oneShot = false;
a->canBeInterrupted = false;
a->resolve();
SAFE_DELETE(a);
MTGAbility * a2 = NEW ABlinkGeneric(game, game->mLayers->actionLayer()->getMaxId(), source, cardTwo, blinkueot, blinkForSource, blinkhand, stored);
a2->target = (Targetable*)cardTwo;
a2->oneShot = false;
a2->canBeInterrupted = false;
a2->resolve();
SAFE_DELETE(a2);
this->forceDestroy = 1;
this->removeFromGame();
return;
}
else
_target->controller()->game->putInZone(_target, _target->currentZone,
_target->owner->game->exile);
if (_target->MeldedFrom.size() || !_target)
{
return;
}
if(_target->isToken)
{
//if our target is a token, we're done as soon as its sent to exile.
this->forceDestroy = 1;
return;
}
if (_target && _target->next)
_target = _target->next;
_target->blinked = true;
Blinked = _target;
if (!blinkueot && !blinkForSource)
{
returnCardIntoPlay(_target);
}
}
}
void ABlink::returnCardIntoPlay(MTGCardInstance* _target) {
MTGCardInstance * Blinker = NULL;
if(!_target->blinked)
{
this->forceDestroy = 1;
return;
}
if (!blinkhand)
Blinker = _target->controller()->game->putInZone(
_target,
_target->currentZone,
_target->owner->game->battlefield);
if (blinkhand)
{
_target->controller()->game->putInZone(
_target,
_target->currentZone,
_target->owner->game->hand);
return;
}
Spell * spell = NEW Spell(game, Blinker);
spell->source->counters->init();
if (spell->source->hasSubtype(Subtypes::TYPE_AURA) && !blinkhand)
{
TargetChooserFactory tcf(game);
TargetChooser * tc = tcf.createTargetChooser(
spell->source->spellTargetType,
spell->source);
if (!tc->validTargetsExist())
{
spell->source->owner->game->putInExile(spell->source);
SAFE_DELETE(spell);
SAFE_DELETE(tc);
this->forceDestroy = 1;
return;
}
/*MTGGameZone * inplay = spell->source->owner->game->inPlay;
spell->source->target = NULL;
for (int i = game->getRandomGenerator()->random()%inplay->nb_cards;;i = game->getRandomGenerator()->random()%inplay->nb_cards)
{
if(tc->canTarget(inplay->cards[i]) && spell->source->target == NULL)
{
spell->source->target = inplay->cards[i];
spell->getNextCardTarget();
spell->resolve();
SAFE_DELETE(spell);
SAFE_DELETE(tc);
this->forceDestroy = 1;
return;
}
}*/
//replaced with castcard(putinplay)
MTGAbility *a = NEW AACastCard(game, game->mLayers->actionLayer()->getMaxId(), Blinker, Blinker,false,false,false,"","Return to Play",false,true);
a->oneShot = false;
a->canBeInterrupted = false;
a->addToGame();
SAFE_DELETE(spell);
SAFE_DELETE(tc);
this->forceDestroy = 1;
return;
}
spell->source->power = spell->source->origpower;
spell->source->toughness = spell->source->origtoughness;
spell->source->X = 0;
if (!spell->source->hasSubtype(Subtypes::TYPE_AURA)) {
spell->resolve();
if (stored)
{
MTGAbility * clonedStored = stored->clone();
clonedStored->target = spell->source;
if (clonedStored->oneShot)
{
clonedStored->resolve();
delete (clonedStored);
}
else
{
clonedStored->addToGame();
}
}
}
SAFE_DELETE(spell);
SAFE_DELETE(tc);
SAFE_DELETE(stored);
this->forceDestroy = 1;
Blinked = NULL;
}
int ABlink::resolve()
{
return 0;
}
const string ABlink::getMenuText()
{
return "Blink";
}
ABlink * ABlink::clone() const
{
ABlink * a = NEW ABlink(*this);
a->stored = stored ? stored->clone() : NULL;
a->forceDestroy = -1;
return a;
};
ABlink::~ABlink()
{
SAFE_DELETE(stored);
}
ABlinkGeneric::ABlinkGeneric(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target,bool blinkueot,bool blinkForSource,bool blinkhand,MTGAbility * stored) :
InstantAbility(observer, _id, source, _target)
{
ability = NEW ABlink(observer, _id,card,_target,blinkueot,blinkForSource,blinkhand,stored);
}
int ABlinkGeneric::resolve()
{
ABlink * a = ability->clone();
a->target = target;
a->addToGame();
return 1;
}
const string ABlinkGeneric::getMenuText()
{
return "Blink";
}
ABlinkGeneric * ABlinkGeneric::clone() const
{
ABlinkGeneric * a = NEW ABlinkGeneric(*this);
a->ability = this->ability->clone();
a->oneShot = 1;
return a;
}
ABlinkGeneric::~ABlinkGeneric()
{
SAFE_DELETE(ability);
}
// target becomes blocked by source
AABlock::AABlock(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost *) :
InstantAbility(observer, id, card, target)
{
target = _target;
}
int AABlock::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
source = (MTGCardInstance*)source;
if (_target && source->canBlock(_target))
{
source->toggleDefenser(_target);
source->getObserver()->isInterrupting = NULL;
}
return 1;
}
AABlock * AABlock::clone() const
{
return NEW AABlock(*this);
}
// target becomes pair of source
PairCard::PairCard(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost *) :
InstantAbility(observer, id, card, target)
{
target = _target;
oneShot = true;
forceDestroy = 1;
}
int PairCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
source = (MTGCardInstance*)source;
if (_target && !_target->myPair && source)
{
source->myPair = _target;
_target->myPair = source;
}
return 1;
}
PairCard * PairCard::clone() const
{
return NEW PairCard(*this);
}
//target is dredged
dredgeCard::dredgeCard(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost *) :
InstantAbility(observer, id, card, target)
{
target = _target;
oneShot = true;
forceDestroy = 1;
}
int dredgeCard::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if(_target)
{
for(int j = 0; j < _target->data->dredge();j++)
{
_target->controller()->game->putInZone(
_target->controller()->game->library->cards[_target->controller()->game->library->nb_cards - 1],
_target->controller()->game->library, _target->controller()->game->graveyard);
}
_target->controller()->game->putInZone(_target,_target->currentZone,_target->controller()->game->hand);
}
return 1;
}
dredgeCard * dredgeCard::clone() const
{
return NEW dredgeCard(*this);
}
// target becomes a parent of card(source)
AAConnect::AAConnect(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost *) :
InstantAbility(observer, id, card, target)
{
target = _target;
}
int AAConnect::resolve()
{
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target)
{
while (_target->next)
_target = _target->next;
_target->childrenCards.push_back(source);
source->parentCards.push_back(_target);
//weapon
if(source->hasSubtype(Subtypes::TYPE_EQUIPMENT))
{
for (size_t i = 1; i < game->mLayers->actionLayer()->mObjects.size(); i++)
{
MTGAbility * a = ((MTGAbility *) game->mLayers->actionLayer()->mObjects[i]);
AEquip * eq = dynamic_cast<AEquip*> (a);
if (eq && eq->source == source)
{
((AEquip*)a)->unequip();
((AEquip*)a)->equip(_target);
}
}
}
else
{
if(source->target)
source->target = NULL;
//clearing the source target allows us to use target= line
//without creating side effects on any other abilities a card has
//connect has to be the first ability in the cards lines unless you want it to do effects to the targeted card!!!
}
}
return 1;
}
AAConnect * AAConnect::clone() const
{
return NEW AAConnect(*this);
}
AEquip::AEquip(GameObserver* observer, int _id, MTGCardInstance * _source, ManaCost * _cost, int restrictions) :
TargetAbility(observer, _id, _source, NULL, _cost, restrictions)
{
aType = MTGAbility::STANDARD_EQUIP;
isAttach = restrictions != ActivatedAbility::AS_SORCERY;
}
int AEquip::unequip()
{
if (source->target)
{
source->target->equipment -= 1;
source->parentCards.clear();
for (unsigned int w = 0; w < source->target->childrenCards.size(); w++)
{
MTGCardInstance * child = source->target->childrenCards[w];
if (child == source)
source->target->childrenCards.erase(source->target->childrenCards.begin() + w);
}
}
source->target = NULL;
for (size_t i = 0; i < currentAbilities.size(); ++i)
{
MTGAbility * a = currentAbilities[i];
if (dynamic_cast<AEquip *> (a) || dynamic_cast<ATeach *> (a) || dynamic_cast<AAConnect *> (a)
|| dynamic_cast<AANewTarget *> (AbilityFactory::getCoreAbility(a))
|| (a->aType == MTGAbility::STANDARD_TOKENCREATOR && a->oneShot))
{
SAFE_DELETE(a);
continue;
}
game->removeObserver(currentAbilities[i]);
}
currentAbilities.clear();
WEvent * e = NEW WEventCardUnattached(source);
game->receiveEvent(e);
return 1;
}
int AEquip::equip(MTGCardInstance * equipped)
{
source->target = equipped;
source->target->equipment += 1;
source->parentCards.push_back(equipped);
source->target->childrenCards.push_back((MTGCardInstance*)source);
AbilityFactory af(game);
af.getAbilities(&currentAbilities, NULL, source);
for (size_t i = 0; i < currentAbilities.size(); ++i)
{
MTGAbility * a = currentAbilities[i];
if (dynamic_cast<AEquip *> (a)) continue;
if (dynamic_cast<ATeach *> (a)) continue;
if (dynamic_cast<AAConnect *> (a)) continue;
if (dynamic_cast<AANewTarget *> (af.getCoreAbility(a))) continue;
if (a->aType == MTGAbility::STANDARD_TOKENCREATOR && a->oneShot)
{
a->forceDestroy = 1;
continue;
}
if (dynamic_cast<AACopier *> (af.getCoreAbility(a)))
{
a->forceDestroy = 1;
continue;
}
//we generally dont want to pass oneShot tokencreators to the cards
//we equip...
a->addToGame();
}
WEvent * e = NEW WEventCardEquipped(source);
game->receiveEvent(e);
return 1;
}
int AEquip::resolve()
{
MTGCardInstance * mTarget = tc->getNextCardTarget();
if (!mTarget) return 0;
if (mTarget == source) return 0;
unequip();
equip(mTarget);
return 1;
}
const string AEquip::getMenuText()
{
if (isAttach)
return "Attach";
else
return "Equip";
}
int AEquip::testDestroy()
{
if (source->target && !game->isInPlay(source->target))
unequip();
if (!game->connectRule)
{
if (source->target && TargetAbility::tc && !TargetAbility::tc->canTarget((Targetable *)source->target,true))
unequip();
}
return TargetAbility::testDestroy();
}
int AEquip::destroy()
{
unequip();
return TargetAbility::destroy();
}
AEquip * AEquip::clone() const
{
return NEW AEquip(*this);
}
// casting a card for free, or casting a copy of a card.
AACastCard::AACastCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target,bool _restricted,bool _copied,bool asNormal,string _namedCard,string _name,bool _noEvent,bool putinplay,bool madness) :
MTGAbility(observer, _id, _source),restricted(_restricted),asCopy(_copied),normal(asNormal),cardNamed(_namedCard),nameThis(_name),noEvent(_noEvent),putinplay(putinplay), asNormalMadness(madness)
{
target = _target;
andAbility = NULL;
processed = false;
theNamedCard = NULL;
}
void AACastCard::Update(float dt)
{
MTGAbility::Update(dt);
if (processed)
return;
if(cardNamed.size() && !theNamedCard)
{
if (cardNamed.find("imprintedcard") != string::npos)
{
if (source && source->currentimprintName.size())
{
cardNamed = source->currentimprintName;
}
}
theNamedCard = makeCard();
}
if(putinplay)
{
MTGCardInstance * toCheck = (MTGCardInstance*)target;
toCheck->target = NULL;
toCheck->playerTarget = NULL;
toCheck->bypassTC = true;
TargetChooserFactory tcf(game);
TargetChooser * atc = tcf.createTargetChooser(toCheck->spellTargetType,toCheck);
if ((toCheck->hasType(Subtypes::TYPE_AURA) && !atc->validTargetsExist())||toCheck->isToken)
{
processed = true;
this->forceDestroy = 1;
return ;
}
SAFE_DELETE(atc);
}
if (restricted)
{
MTGCardInstance * toCheck = (MTGCardInstance*)target;
if(theNamedCard)
toCheck = theNamedCard;
if (game->currentActionPlayer->game->playRestrictions->canPutIntoZone(toCheck, source->controller()->game->stack) == PlayRestriction::CANT_PLAY)
{
processed = true;
this->forceDestroy = 1;
return ;
}
if(!allowedToCast(toCheck,source->controller()))
{
processed = true;
this->forceDestroy = 1;
return;
}
/*if(!toCheck->hasType(Subtypes::TYPE_INSTANT) && !(game->getCurrentGamePhase() == MTG_PHASE_FIRSTMAIN || game->getCurrentGamePhase() == MTG_PHASE_SECONDMAIN))
{
processed = true;
this->forceDestroy = 1;
return;
}*/
}
MTGCardInstance * toCheck = (MTGCardInstance*)target;
if(theNamedCard)
toCheck = theNamedCard;
if(toCheck && toCheck->spellTargetType.size())
{
TargetChooserFactory tcf(game);
TargetChooser * stc = tcf.createTargetChooser(toCheck->spellTargetType,toCheck);
if (!stc->validTargetsExist()||toCheck->isToken)
{
processed = true;
this->forceDestroy = 1;
return;
}
SAFE_DELETE(stc);
}
if (Spell * checkSpell = dynamic_cast<Spell*>(target))
{
toCheck = checkSpell->source;
}
if (!game->targetListIsSet(toCheck))
{
if(game->targetChooser)
game->targetChooser->Owner = source->controller();//sources controller is the caster
return;
}
resolveSpell();
this->forceDestroy = 1;
return;
}
int AACastCard::isReactingToTargetClick(Targetable * card){return 0;}
int AACastCard::reactToTargetClick(Targetable * object)
{
if (MTGCardInstance * cObject = dynamic_cast<MTGCardInstance *>(object))
return reactToClick(cObject);
if (waitingForAnswer)
{
if (tc->toggleTarget(object) == TARGET_OK_FULL)
{
waitingForAnswer = 0;
game->mLayers->actionLayer()->setCurrentWaitingAction(NULL);
return MTGAbility::reactToClick(source);
}
return 1;
}
return 0;
}
MTGCardInstance * AACastCard::makeCard()
{
MTGCardInstance * card = NULL;
MTGCard * cardData = MTGCollection()->getCardByName(cardNamed);
card = NEW MTGCardInstance(cardData, source->controller()->game);
card->owner = source->controller();
source->controller()->game->temp->addCard(card);
return card;
}
int AACastCard::resolveSpell()
{
if (processed)
return 0;
MTGCardInstance * _target = (MTGCardInstance *) target;
if(theNamedCard)
_target = theNamedCard;
if (Spell * checkSpell = dynamic_cast<Spell*>(target))
{
_target = checkSpell->source;
}
if(asCopy)
{
MTGCard * cardToCopy = MTGCollection()->getCardById(_target->getId());
MTGCardInstance * myDummy = NULL;
myDummy = NEW MTGCardInstance(cardToCopy, source->controller()->game);
myDummy->setObserver(source->controller()->getObserver());
source->controller()->game->garbage->addCard(myDummy);
_target = myDummy;
_target->isToken = 1;
_target->changeController(source->controller(),true);
}
if (_target)
{
if (_target->isLand())
{
MTGCardInstance * copy = _target->controller()->game->putInZone(_target, _target->currentZone, source->controller()->game->temp,noEvent);
copy->changeController(source->controller(),true);
Spell * spell = NEW Spell(game, 0,copy,NULL,NULL, 1);
spell->resolve();
delete spell;
}
else
{
Spell * spell = NULL;
MTGCardInstance * copy = NULL;
if ((normal || asNormalMadness)||(!_target->hasType(Subtypes::TYPE_INSTANT) && !_target->hasType(Subtypes::TYPE_SORCERY)))
{
if (putinplay && (_target->hasType(Subtypes::TYPE_ARTIFACT)||_target->hasType(Subtypes::TYPE_CREATURE)||_target->hasType(Subtypes::TYPE_ENCHANTMENT)||_target->hasType(Subtypes::TYPE_PLANESWALKER)))
copy =_target->controller()->game->putInZone(_target, _target->currentZone, source->controller()->game->battlefield,noEvent);
else
copy =_target->controller()->game->putInZone(_target, _target->currentZone, source->controller()->game->stack,noEvent);
copy->changeController(source->controller(),true);
if(asNormalMadness)
copy->MadnessPlay = true;
}
else
{
if (putinplay && (_target->hasType(Subtypes::TYPE_ARTIFACT)||_target->hasType(Subtypes::TYPE_CREATURE)||_target->hasType(Subtypes::TYPE_ENCHANTMENT)||_target->hasType(Subtypes::TYPE_PLANESWALKER)))
copy =_target->controller()->game->putInZone(_target, _target->currentZone, source->controller()->game->battlefield,noEvent);
else
copy =_target->controller()->game->putInZone(_target, _target->currentZone, _target->controller()->game->stack,noEvent);
copy->changeController(source->controller(),true);
}
if (game->targetChooser)
{
game->targetChooser->Owner = source->controller();
spell = game->mLayers->stackLayer()->addSpell(copy, game->targetChooser, NULL, 1, 0);
game->targetChooser = NULL;
}
else
{
spell = game->mLayers->stackLayer()->addSpell(copy, NULL, NULL, 1, 0);
}
if (copy->has(Constants::STORM))
{
int storm = _target->controller()->game->stack->seenThisTurn("*", Constants::CAST_ALL) + source->controller()->opponent()->game->stack->seenThisTurn("*", Constants::CAST_ALL);
for (int i = storm; i > 1; i--)
{
spell = game->mLayers->stackLayer()->addSpell(copy, NULL, 0, 1, 1);
}
}
if (!copy->has(Constants::STORM))
{
copy->X = _target->X;
copy->castX = copy->X;
}
if(andAbility)
{
MTGAbility * andAbilityClone = andAbility->clone();
andAbilityClone->target = copy;
if(andAbility->oneShot)
{
andAbilityClone->resolve();
SAFE_DELETE(andAbilityClone);
}
else
{
andAbilityClone->addToGame();
}
}
}
this->forceDestroy = true;
processed = true;
return 1;
}
return 0;
}
const string AACastCard::getMenuText()
{
if(nameThis.size())
return nameThis.c_str();
if(putinplay)
return "Put Into Play";
return "Cast Card";
}
AACastCard * AACastCard::clone() const
{
AACastCard * a = NEW AACastCard(*this);
if(tc)
a->tc = tc->clone();
if(andAbility)
a->andAbility = andAbility->clone();
return a;
}
AACastCard::~AACastCard()
{
SAFE_DELETE(tc);
SAFE_DELETE(andAbility);
}
//Tutorial Messaging
ATutorialMessage::ATutorialMessage(GameObserver* observer, MTGCardInstance * source, string message, int limit)
: MTGAbility(observer, 0, source), IconButtonsController(observer->getInput(), 0, 0), mLimit(limit)
{
mBgTex = NULL;
mElapsed = 0;
mIsImage = false;
for (int i = 0; i < 9; i++)
mBg[i] = NULL;
if(game->getResourceManager())
{
string gfx = game->getResourceManager()->graphicsFile(message);
if (fileExists(gfx.c_str()))
{
mIsImage = true;
mMessage = message;
}
else
{
mMessage = _(message); //translate directly here, remove this and translate at rendering time if it bites us
ReplaceString(mMessage, "\\n", "\n");
}
}
if (mIsImage)
{
mX = SCREEN_WIDTH_F / 2;
mY = SCREEN_HEIGHT_F / 2;
}
else
{
mX = 0;
mY = -SCREEN_HEIGHT_F - 0.1f; //Offscreen
}
mDontShow = mUserCloseRequest = (mLimit > 0) && (alreadyShown() >= mLimit);
if(mDontShow)
forceDestroy = 1;
}
string ATutorialMessage::getOptionName()
{
std::stringstream out;
out << "tuto_";
out << hash_djb2(mMessage.c_str());
return out.str();
}
int ATutorialMessage::alreadyShown()
{
return options[getOptionName()].number;
}
bool ATutorialMessage::CheckUserInput(JButton key)
{
if (mUserCloseRequest) return false;
if(key == JGE_BTN_SEC || key == JGE_BTN_OK)
{
ButtonPressed(0, 1);
return true;
}
//Required for Mouse/touch input
IconButtonsController::CheckUserInput(key);
return true; //this ability is modal, so it catches all key events until it gets closed
}
void ATutorialMessage::Update(float dt)
{
if (!game->mLayers->stackLayer()->getCurrentTutorial() && !mDontShow)
game->mLayers->stackLayer()->setCurrentTutorial(this);
if (game->mLayers->stackLayer()->getCurrentTutorial() != this)
return;
if (mUserCloseRequest && mY < -SCREEN_HEIGHT)
mDontShow = true;
if (mDontShow)
{
game->mLayers->stackLayer()->setCurrentTutorial(0);
forceDestroy = 1;
return;
}
mElapsed += dt;
if(!mUserCloseRequest)
IconButtonsController::Update(dt);
if (mIsImage)
return;
//Below this only affects "text" mode
if (!mUserCloseRequest && mY < 0)
{
mY = -SCREEN_HEIGHT + (SCREEN_HEIGHT * mElapsed / 0.75f); //Todo: more physical drop-in.
if (mY >= 0)
mY = 0;
}
else if (mUserCloseRequest && mY > -SCREEN_HEIGHT)
{
mY = -(SCREEN_HEIGHT * mElapsed / 0.75f);
}
}
void ATutorialMessage::ButtonPressed(int, int)
{
//TODO : cancel ALL tips/tutorials for JGE_BTN_SEC?
if (mLimit)
{
string optionName = getOptionName();
options[optionName].number = options[optionName].number + 1;
options.save(); //TODO: if we experience I/O slowness in tutorials, move this save at the end of a turn, or at the end of the game.
}
mElapsed = 0;
mUserCloseRequest = true;
}
void ATutorialMessage::Render()
{
if (mDontShow)
return;
if (mY < -SCREEN_HEIGHT)
return;
if (!mBgTex)
{
if (mIsImage)
{
mBgTex = game->getResourceManager()->RetrieveTexture(mMessage, RETRIEVE_LOCK);
if (mBgTex)
{
mBg[0] = NEW JQuad(mBgTex, 0, 0, (float) mBgTex->mWidth, (float) mBgTex->mHeight);
mBg[0]->SetHotSpot(mBg[0]->mWidth / 2, mBg[0]->mHeight / 2);
//Continue Button
JQuadPtr quad = game->getResourceManager()->RetrieveQuad("iconspsp.png", 4 * 32, 0, 32, 32, "iconpsp4", RETRIEVE_MANAGE);
quad->SetHotSpot(16, 16);
IconButton * iconButton = NEW IconButton(1, this, quad.get(), 0, mBg[0]->mHeight / 2, 0.7f, Fonts::MAGIC_FONT, _("continue"), 0, 16, true);
Add(iconButton);
}
if (options[Options::SFXVOLUME].number > 0)
{
game->getResourceManager()->PlaySample("tutorial.wav");
}
}
else
{
mBgTex = game->getResourceManager()->RetrieveTexture("taskboard.png", RETRIEVE_LOCK);
float unitH = static_cast<float> (mBgTex->mHeight / 4);
float unitW = static_cast<float> (mBgTex->mWidth / 4);
if (unitH == 0 || unitW == 0) return;
if (mBgTex)
{
mBg[0] = NEW JQuad(mBgTex, 0, 0, unitW, unitH);
mBg[1] = NEW JQuad(mBgTex, unitW, 0, unitW * 2, unitH);
mBg[2] = NEW JQuad(mBgTex, unitW * 3, 0, unitW, unitH);
mBg[3] = NEW JQuad(mBgTex, 0, unitH, unitW, unitH * 2);
mBg[4] = NEW JQuad(mBgTex, unitW, unitH, unitW * 2, unitH * 2);
mBg[5] = NEW JQuad(mBgTex, unitW * 3, unitH, unitW, unitH * 2);
mBg[6] = NEW JQuad(mBgTex, 0, unitH * 3, unitW, unitH);
mBg[7] = NEW JQuad(mBgTex, unitW, unitH * 3, unitW * 2, unitH);
mBg[8] = NEW JQuad(mBgTex, unitW * 3, unitH * 3, unitW, unitH);
}
//Continue Button
JQuadPtr quad = game->getResourceManager()->RetrieveQuad("iconspsp.png", 4 * 32, 0, 32, 32, "iconpsp4", RETRIEVE_MANAGE);
quad->SetHotSpot(16, 16);
IconButton * iconButton = NEW IconButton(1, this, quad.get(), SCREEN_WIDTH_F / 2, SCREEN_HEIGHT_F - 60, 0.7f, Fonts::MAGIC_FONT, _("continue"), 0, 16, true);
Add(iconButton);
mSH = 64 / unitH;
mSW = 64 / unitW;
if (options[Options::SFXVOLUME].number > 0)
{
game->getResourceManager()->PlaySample("chain.wav");
}
}
}
JRenderer * r = JRenderer::GetInstance();
//Render background board
if (mBgTex)
{
if (mIsImage)
{
int alpha = mUserCloseRequest ? MAX(0, 255 - (int)(mElapsed * 500)) : MIN(255, (int)(mElapsed * 500)) ;
if (mUserCloseRequest && alpha == 0)
mDontShow = true;
r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(alpha / 2,0,0,0));
mBg[0]->SetColor(ARGB(alpha,255,255,255));
r->RenderQuad(mBg[0], SCREEN_WIDTH_F /2 , SCREEN_HEIGHT_F / 2 , 0);
IconButtonsController::SetColor(ARGB(alpha,255,255,255));
}
else
{
//Setup fonts.
WFont * f2 = game->getResourceManager()->GetWFont(Fonts::MAGIC_FONT);
f2->SetColor(ARGB(255, 205, 237, 240));
r->FillRect(0, mY, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
r->RenderQuad(mBg[0], 0, mY, 0, mSW, mSH); //TL
r->RenderQuad(mBg[2], SCREEN_WIDTH - 64, mY, 0, mSW, mSH); //TR
r->RenderQuad(mBg[6], 0, mY + SCREEN_HEIGHT - 64, 0, mSW, mSH); //BL
r->RenderQuad(mBg[8], SCREEN_WIDTH - 64, mY + SCREEN_HEIGHT - 64, 0, mSW, mSH); //BR
//Stretch the sides
float stretchV = (144.0f / 128.0f) * mSH;
float stretchH = (176.0f / 128.0f) * mSW;
r->RenderQuad(mBg[3], 0, mY + 64, 0, mSW, stretchV); //L
r->RenderQuad(mBg[5], SCREEN_WIDTH - 64, mY + 64, 0, mSW, stretchV); //R
r->RenderQuad(mBg[1], 64, mY, 0, stretchH, mSH); //T1
r->RenderQuad(mBg[1], 240, mY, 0, stretchH, mSH); //T1
r->RenderQuad(mBg[7], 64, mY + 208, 0, stretchH, mSH); //B1
r->RenderQuad(mBg[7], 240, mY + 208, 0, stretchH, mSH); //B1
r->RenderQuad(mBg[4], 64, mY + 64, 0, stretchH, stretchV); //Center1
r->RenderQuad(mBg[4], 240, mY + 64, 0, stretchH, stretchV); //Center2
}
}
else
{
r->FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, ARGB(128,0,0,0));
r->FillRect(10, 10 + mY, SCREEN_WIDTH - 10, SCREEN_HEIGHT - 10, ARGB(128,0,0,0));
}
if (!mBgTex || !mIsImage)
{
float posX = 40, posY = mY + 20;
string title = _("Help");
WFont * f = game->getResourceManager()->GetWFont(Fonts::MAGIC_FONT);
WFont * f3 = game->getResourceManager()->GetWFont(Fonts::MENU_FONT); //OPTION_FONT
f->SetColor(ARGB(255, 55, 46, 34));
f3->SetColor(ARGB(255, 219, 206, 151));
f3->DrawString(title.c_str(), static_cast<float> ((SCREEN_WIDTH - 20) / 2 - title.length() * 4), posY);
posY += 30;
f->DrawString(_(mMessage).c_str(), posX, posY);
f->SetScale(1);
}
IconButtonsController::Render();
}
ATutorialMessage * ATutorialMessage::clone() const
{
ATutorialMessage * copy = NEW ATutorialMessage(*this);
copy->mUserCloseRequest = (copy->alreadyShown() > 0);
return copy;
}
ATutorialMessage::~ATutorialMessage()
{
if (mBgTex)
{
game->getResourceManager()->Release(mBgTex);
for (int i = 0; i < 9; i++)
SAFE_DELETE(mBg[i]);
}
}
// utility functions
// Given a delimited string of abilities, add the ones to the list that are "Basic" MTG abilities
void PopulateAbilityIndexVector(list<int>& abilities, const string& abilityStringList, char delimiter)
{
vector<string> abilitiesList = split(abilityStringList, delimiter);
for (vector<string>::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter)
{
int abilityIndex = Constants::GetBasicAbilityIndex(*iter);
if (abilityIndex != -1)
abilities.push_back(abilityIndex);
}
}
void PopulateColorIndexVector(list<int>& colors, const string& colorStringList, char delimiter)
{
vector<string> abilitiesList = split(colorStringList, delimiter);
for (vector<string>::iterator iter = abilitiesList.begin(); iter != abilitiesList.end(); ++iter)
{
for (int colorIndex = Constants::MTG_COLOR_ARTIFACT; colorIndex < Constants::NB_Colors; ++colorIndex)
{
// if the text is not a basic ability but contains a valid color add it to the color vector
if ((Constants::GetBasicAbilityIndex(*iter) == -1)
&& ((*iter).find(Constants::MTGColorStrings[colorIndex]) != string::npos))
colors.push_back(colorIndex);
}
}
}
void PopulateSubtypesIndexVector(list<int>& types, const string& subTypesStringList, char delimiter)
{
vector<string> subTypesList = split(subTypesStringList, delimiter);
for (vector<string>::iterator it = subTypesList.begin(); it != subTypesList.end(); ++it)
{
string subtype = *it;
size_t id = MTGAllCards::findType(subtype);
if (id != string::npos)
types.push_back(id);
}
}