7986 lines
238 KiB
C++
7986 lines
238 KiB
C++
#include "PrecompiledHeader.h"
|
||
#include "AllAbilities.h"
|
||
#include "Translate.h"
|
||
|
||
|
||
//display a text animation, this is not a real ability.
|
||
MTGEventText::MTGEventText(GameObserver* observer, int _id, MTGCardInstance * card, string textToShow) :
|
||
MTGAbility(observer, _id,card)
|
||
{
|
||
textAlpha = 255;
|
||
text = textToShow;
|
||
}
|
||
|
||
void MTGEventText::Update(float dt)
|
||
{
|
||
if (textAlpha)
|
||
{
|
||
textAlpha -= static_cast<int> (200 * dt);
|
||
Render();
|
||
if (textAlpha < 0)
|
||
{
|
||
textAlpha = 0;
|
||
this->forceDestroy = 1;
|
||
}
|
||
}
|
||
MTGAbility::Update(dt);
|
||
}
|
||
|
||
void MTGEventText::Render()
|
||
{
|
||
if (!textAlpha)
|
||
return;
|
||
WFont * mFont = WResourceManager::Instance()->GetWFont(Fonts::OPTION_FONT);
|
||
float backup = mFont->GetScale();
|
||
mFont->SetScale(2 - (float) textAlpha / 130);
|
||
mFont->SetColor(ARGB(255,255,255,255));
|
||
mFont->DrawString(text.c_str(), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, JGETEXT_CENTER);
|
||
mFont->SetScale(backup);
|
||
}
|
||
|
||
MTGEventText * MTGEventText::clone() const
|
||
{
|
||
return NEW MTGEventText(*this);
|
||
}
|
||
|
||
//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(¤tAbilities, NULL, _target, 0);
|
||
for (size_t i = 0; i < currentAbilities.size(); ++i)
|
||
{
|
||
MTGAbility * a = currentAbilities[i];
|
||
a->source = (MTGCardInstance *) _target;
|
||
if( a && dynamic_cast<AAMorph *> (a))
|
||
{
|
||
a->removeFromGame();
|
||
game->removeObserver(a);
|
||
}
|
||
if (a)
|
||
{
|
||
if (a->oneShot)
|
||
{
|
||
a->resolve();
|
||
delete (a);
|
||
}
|
||
else
|
||
{
|
||
a->addToGame();
|
||
MayAbility * dontAdd = dynamic_cast<MayAbility*>(a);
|
||
if(!dontAdd)
|
||
{
|
||
_target->cardsAbilities.push_back(a);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
currentAbilities.clear();
|
||
testDestroy();
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
int AAMorph::testDestroy()
|
||
{
|
||
MTGCardInstance * _target = (MTGCardInstance *) target;
|
||
if(target)
|
||
{
|
||
if(_target->turningOver && !_target->isMorphed && !_target->morphed)
|
||
{
|
||
game->removeObserver(this);
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
const string AAMorph::getMenuText()
|
||
{
|
||
return "Morph";
|
||
}
|
||
|
||
AAMorph * AAMorph::clone() const
|
||
{
|
||
AAMorph * a = NEW AAMorph(*this);
|
||
a->forceDestroy = 1;
|
||
return a;
|
||
}
|
||
|
||
//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(¤tAbilities, NULL, _target);
|
||
for (size_t i = 0; i < currentAbilities.size(); ++i)
|
||
{
|
||
MTGAbility * a = currentAbilities[i];
|
||
a->source = (MTGCardInstance *) _target;
|
||
if (a)
|
||
{
|
||
if (a->oneShot)
|
||
{
|
||
a->resolve();
|
||
SAFE_DELETE(a);
|
||
}
|
||
else
|
||
{
|
||
a->addToGame();
|
||
MayAbility * dontAdd = dynamic_cast<MayAbility*>(a);
|
||
if(!dontAdd)
|
||
{
|
||
_target->cardsAbilities.push_back(a);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
//power
|
||
int powerMod = 0;
|
||
int toughMod = 0;
|
||
bool powerlessThanOriginal = false;
|
||
bool toughLessThanOriginal = false;
|
||
if(_target->power < _target->origpower)
|
||
{
|
||
powerMod = _target->origpower - _target->power;
|
||
powerlessThanOriginal = true;
|
||
}
|
||
else
|
||
{
|
||
powerMod =_target->power - _target->origpower;
|
||
}
|
||
//toughness
|
||
if(_target->toughness <= _target->origtoughness)
|
||
{
|
||
toughMod = _target->origtoughness - _target->toughness;
|
||
toughLessThanOriginal = true;
|
||
}
|
||
else
|
||
{
|
||
toughMod =_target->toughness - _target->origtoughness;
|
||
}
|
||
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 can’t, sacrifice the permanent.”
|
||
|
||
// Comprehensive 702.62a:
|
||
// Vanishing is a keyword that represents three abilities.
|
||
// "Vanishing N" means "This permanent comes into play with N time counters on it,"
|
||
// "At the beginning of your upkeep, if this permanent has a time counter on it, remove a time counter from it," and
|
||
// "When the last time counter is removed from this permanent, sacrifice it."
|
||
AVanishing::AVanishing(GameObserver* observer, int _id, MTGCardInstance * card, ManaCost *, int, int amount, string counterName) :
|
||
MTGAbility(observer, _id, source, target),amount(amount),counterName(counterName)
|
||
{
|
||
target = card;
|
||
source = card;
|
||
for (int i = 0; i < amount; i++)
|
||
source->counters->addCounter(counterName.c_str(), 0, 0);
|
||
}
|
||
|
||
void AVanishing::Update(float dt)
|
||
{
|
||
if (newPhase != currentPhase && source->controller() == game->currentPlayer)
|
||
{
|
||
if (newPhase == MTG_PHASE_UPKEEP)
|
||
{
|
||
bool hasCounters = (source->counters && source->counters->hasCounter(counterName.c_str(), 0, 0));
|
||
if (hasCounters)
|
||
{
|
||
// sacrifice of card with time counter is handled in removeCounter
|
||
source->counters->removeCounter(counterName.c_str(), 0, 0);
|
||
} else
|
||
{
|
||
if (counterName == "fade")
|
||
{
|
||
MTGCardInstance * beforeCard = source;
|
||
source->controller()->game->putInGraveyard(source);
|
||
WEvent * e = NEW WEventCardSacrifice(beforeCard,source);
|
||
game->receiveEvent(e);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
MTGAbility::Update(dt);
|
||
}
|
||
|
||
int AVanishing::resolve()
|
||
{
|
||
return 1;
|
||
}
|
||
|
||
const string AVanishing::getMenuText()
|
||
{
|
||
if (counterName.find("fade") != string::npos)
|
||
{
|
||
return "Fading";
|
||
}
|
||
return "Vanishing";
|
||
}
|
||
|
||
AVanishing * AVanishing::clone() const
|
||
{
|
||
return NEW AVanishing(*this);
|
||
}
|
||
|
||
AVanishing::~AVanishing()
|
||
{
|
||
}
|
||
|
||
//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(¤tAbilities, NULL, source);
|
||
for (size_t i = 0; i < currentAbilities.size(); ++i)
|
||
{
|
||
MTGAbility * a = currentAbilities[i];
|
||
if (dynamic_cast<AEquip *> (a)) continue;
|
||
if (dynamic_cast<ATeach *> (a)) continue;
|
||
if (dynamic_cast<AAConnect *> (a)) continue;
|
||
if (dynamic_cast<AANewTarget *> (af.getCoreAbility(a))) continue;
|
||
if (a->aType == MTGAbility::STANDARD_TOKENCREATOR && a->oneShot)
|
||
{
|
||
a->forceDestroy = 1;
|
||
continue;
|
||
}
|
||
if (dynamic_cast<AACopier *> (af.getCoreAbility(a)))
|
||
{
|
||
a->forceDestroy = 1;
|
||
continue;
|
||
}
|
||
//we generally dont want to pass oneShot tokencreators to the cards
|
||
//we equip...
|
||
a->addToGame();
|
||
}
|
||
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);
|
||
}
|
||
}
|