change some cards since -cantattack overrides cantattack. If there is an effect that makes creatures can't attack, you can just activate the ability to remove -cantattack...
6782 lines
203 KiB
C++
6782 lines
203 KiB
C++
#ifndef _CARDS_H_
|
|
#define _CARDS_H_
|
|
|
|
#include "DebugRoutines.h"
|
|
#include "MTGAbility.h"
|
|
#include "ManaCost.h"
|
|
#include "CardDescriptor.h"
|
|
#include "AIPlayer.h"
|
|
#include "CardDisplay.h"
|
|
#include "Subtypes.h"
|
|
#include "CardGui.h"
|
|
#include "Token.h"
|
|
#include "Counters.h"
|
|
#include "WEvent.h"
|
|
#include "GuiStatic.h"
|
|
#include "GameObserver.h"
|
|
#include "Subtypes.h"
|
|
#include "ThisDescriptor.h"
|
|
#include <JGui.h>
|
|
#include <hge/hgeparticle.h>
|
|
#include "IconButton.h"
|
|
#include "ExtraCost.h"
|
|
|
|
#include <map>
|
|
using std::map;
|
|
|
|
//
|
|
// Misc classes
|
|
//
|
|
class MTGEventText: public MTGAbility
|
|
{
|
|
public:
|
|
int textAlpha;
|
|
string text;
|
|
void Update(float dt);
|
|
void Render();
|
|
MTGEventText(GameObserver* observer, int _id,MTGCardInstance * card, string text);
|
|
virtual MTGEventText * clone() const;
|
|
};
|
|
|
|
class MTGRevealingCards : public MTGAbility, public CardDisplay
|
|
{
|
|
public:
|
|
vector<CardView*> cards;
|
|
Player * playerForZone;
|
|
MTGGameZone * RevealZone;
|
|
MTGGameZone * RevealFromZone;
|
|
string revealCertainTypes;
|
|
string revealUntil;
|
|
|
|
CardDisplay * revealDisplay;
|
|
vector<CardDisplay*>trashDisplays;//used for repeat
|
|
int nbCard;
|
|
string abilityString;
|
|
string number;
|
|
string abilityOne;
|
|
string abilityTwo;
|
|
string afterReveal;
|
|
bool afterEffectActivated;
|
|
MTGAbility * abilityToCast;
|
|
MTGAbility * abilityFirst;
|
|
MTGAbility * abilitySecond;
|
|
MTGAbility * abilityAfter;
|
|
vector<MTGAbility*>abilities;
|
|
bool repeat;//only the first ability can be repeated, and it must be targeted.
|
|
bool initCD;
|
|
|
|
void Update(float dt);
|
|
int testDestroy();
|
|
int toResolve();
|
|
void CardViewBackup(MTGCardInstance * backup);
|
|
void Render();
|
|
bool CheckUserInput(JButton key);
|
|
MTGAbility * contructAbility(string abilityToMake = "");
|
|
MTGRevealingCards(GameObserver* observer, int _id, MTGCardInstance * card, string text);
|
|
virtual MTGRevealingCards * clone() const;
|
|
~MTGRevealingCards();
|
|
int receiveEvent(WEvent*);
|
|
};
|
|
|
|
class RevealDisplay : public CardDisplay
|
|
{
|
|
public:
|
|
RevealDisplay(int id, GameObserver* game, int x, int y, JGuiListener * listener = NULL, TargetChooser * tc = NULL,
|
|
int nb_displayed_items = 7);
|
|
void AddCard(MTGCardInstance * _card);
|
|
bool CheckUserInput(JButton key);
|
|
};
|
|
|
|
class GenericRevealAbility : public ActivatedAbility
|
|
{
|
|
public:
|
|
string howMany;
|
|
MTGRevealingCards * ability;
|
|
GenericRevealAbility(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target, string _howMany);
|
|
int resolve();
|
|
const string getMenuText();
|
|
GenericRevealAbility * clone() const;
|
|
~GenericRevealAbility();
|
|
|
|
};
|
|
|
|
class MTGScryCards : public MTGAbility, public CardDisplay
|
|
{
|
|
public:
|
|
vector<CardView*> cards;
|
|
MTGGameZone * RevealZone;
|
|
MTGGameZone * RevealFromZone;
|
|
|
|
CardDisplay * revealDisplay;
|
|
vector<CardDisplay*>trashDisplays;//used for repeat
|
|
int nbCard;
|
|
bool delayed;
|
|
bool dontRevealAfter;
|
|
int revealTopAmount;
|
|
string delayedAbilityString;
|
|
string abilityString;
|
|
string number;
|
|
string abilityOne;
|
|
string abilityTwo;
|
|
MTGAbility * abilityToCast;
|
|
MTGAbility * abilityFirst;
|
|
MTGAbility * abilitySecond;
|
|
vector<MTGAbility*>abilities;
|
|
bool initCD;
|
|
void Update(float dt);
|
|
int testDestroy();
|
|
void initDisplay(int value = 0);
|
|
int toResolve();
|
|
void Render();
|
|
bool CheckUserInput(JButton key);
|
|
MTGAbility * contructAbility(string abilityToMake = "");
|
|
MTGScryCards(GameObserver* observer, int _id, MTGCardInstance * card, string text);
|
|
virtual MTGScryCards * clone() const;
|
|
~MTGScryCards();
|
|
int receiveEvent(WEvent*);
|
|
};
|
|
|
|
class GenericScryAbility : public ActivatedAbility
|
|
{
|
|
public:
|
|
string howMany;
|
|
MTGScryCards * ability;
|
|
GenericScryAbility(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target, string _howMany);
|
|
int resolve();
|
|
const string getMenuText();
|
|
GenericScryAbility * clone() const;
|
|
~GenericScryAbility();
|
|
|
|
};
|
|
|
|
class WParsedInt
|
|
{
|
|
public:
|
|
int intValue;
|
|
|
|
int computeX(Spell * spell, MTGCardInstance * card)
|
|
{
|
|
if (spell) return spell->computeX(card);
|
|
if (card) return card->X;
|
|
return 1; //this should only hapen when the ai calls the ability. This is to give it an idea of the "direction" of X (positive/negative)
|
|
}
|
|
|
|
private:
|
|
void init(string s, Spell * spell, MTGCardInstance * card)
|
|
{
|
|
if(!s.size())
|
|
return;
|
|
if (!card)
|
|
{
|
|
intValue = atoi(s.c_str());//if there is no card, try parsing a number.
|
|
return;
|
|
}
|
|
MTGCardInstance * target = card->target;
|
|
if(!card->storedCard)
|
|
card->storedCard = card->storedSourceCard;
|
|
intValue = 0;
|
|
bool halfup = false;
|
|
bool halfdown = false;
|
|
bool twice = false;
|
|
bool thrice = false;
|
|
bool other = false;//othertype:[subtype]
|
|
|
|
if (!target) target = card;
|
|
int multiplier = 1;
|
|
if (s[0] == '-')
|
|
{
|
|
s = s.substr(1);
|
|
multiplier = -1;
|
|
if(s.find("stored") != string::npos)
|
|
{
|
|
string altered ="-";
|
|
altered.append(s.substr(+6));
|
|
return init(altered,spell,card->storedCard);
|
|
}
|
|
}
|
|
if(s[0] == '+')
|
|
{
|
|
//ignore "+" signs....
|
|
s = s.substr(1);
|
|
}
|
|
if(s.find("stored") != string::npos)
|
|
{
|
|
return init(s.substr(+6),spell,card->storedCard);
|
|
}
|
|
//rounding values, the words can be written anywhere in the line,
|
|
//they are erased after parsing.
|
|
if(s.find("halfup") != string::npos)
|
|
{
|
|
halfup = true;
|
|
size_t hU = s.find("halfup");
|
|
s.erase(hU,hU + 6);
|
|
}
|
|
if(s.find("halfdown") != string::npos)
|
|
{
|
|
halfdown = true;
|
|
size_t hD = s.find("halfdown");
|
|
s.erase(hD,hD + 8);
|
|
}
|
|
if(s.find("twice") != string::npos)
|
|
{
|
|
twice = true;
|
|
size_t tXX = s.find("twice");
|
|
s.erase(tXX,tXX + 5);
|
|
}
|
|
if(s.find("thrice") != string::npos)
|
|
{
|
|
thrice = true;
|
|
size_t tXXX = s.find("thrice");
|
|
s.erase(tXXX,tXXX + 6);
|
|
}
|
|
|
|
if(s.find("othertype") != string::npos)
|
|
{
|
|
other = true;
|
|
size_t oth = s.find("othertype");
|
|
s.erase(oth,oth + 5);
|
|
}
|
|
if(s.find("otherpower") != string::npos)
|
|
{
|
|
other = true;
|
|
size_t otp = s.find("otherpower");
|
|
s.erase(otp,otp + 5);
|
|
}
|
|
if(s.find("othertoughness") != string::npos)
|
|
{
|
|
other = true;
|
|
size_t ott = s.find("othertoughness");
|
|
s.erase(ott,ott + 5);
|
|
}
|
|
if(s.find("otherconvertedcost") != string::npos)
|
|
{
|
|
other = true;
|
|
size_t otc = s.find("otherconvertedcost");
|
|
s.erase(otc,otc + 5);
|
|
}
|
|
|
|
if (s.find("plusend") != string::npos || s.find("minusend") != string::npos || s.find("math") != string::npos)
|
|
{
|
|
//plus#plusend and minus#minusend splits the first part and second parts and parses the
|
|
//ints for each part, then either adds or subtracts those 2 variables as specified.
|
|
vector<string>mathFound = parseBetween(s, "math", "mathend", true);
|
|
if (mathFound.size())//maths allows us to get the value before applying multipliers
|
|
{
|
|
WParsedInt numPar(mathFound[1], NULL, card);
|
|
intValue = numPar.getValue();
|
|
|
|
}
|
|
else
|
|
{
|
|
vector<string>plusSplit = parseBetween(s, "", "plus", true);
|
|
if (plusSplit.size())
|
|
{
|
|
WParsedInt numPar(plusSplit[1], NULL, card);
|
|
intValue = numPar.getValue();
|
|
}
|
|
vector<string>plusFound = parseBetween(s, "plus", "plusend", true);
|
|
if (plusFound.size())
|
|
{
|
|
WParsedInt numPar(plusFound[1], NULL, card);
|
|
intValue += numPar.getValue();
|
|
}
|
|
vector<string>minusSplit = parseBetween(s, "", "minus", true);
|
|
if (minusSplit.size())
|
|
{
|
|
WParsedInt numPar(minusSplit[1], NULL, card);
|
|
intValue = numPar.getValue();
|
|
}
|
|
vector<string>minusFound = parseBetween(s, "minus", "minusend", true);
|
|
if (minusFound.size())
|
|
{
|
|
WParsedInt numPar(minusFound[1], NULL, card);
|
|
intValue -= numPar.getValue();
|
|
}
|
|
}
|
|
}
|
|
else if(s == "prex")
|
|
{
|
|
if (card->setX > -1)
|
|
{
|
|
intValue = card->setX;
|
|
}
|
|
else
|
|
{
|
|
ManaCost * cX = card->controller()->getManaPool()->Diff(card->getManaCost());
|
|
intValue = cX->getCost(Constants::NB_Colors);
|
|
delete cX;
|
|
}
|
|
}
|
|
else if (s == "x" || s == "X")
|
|
{
|
|
intValue = computeX(spell, card);
|
|
if(intValue < 0)
|
|
intValue = 0;
|
|
}
|
|
else if (s == "xx" || s == "XX")
|
|
{
|
|
intValue = computeX(spell, card) / 2;
|
|
if(intValue < 0)
|
|
intValue = 0;
|
|
}
|
|
else if (s == "castx")
|
|
{
|
|
intValue = card->castX;
|
|
}
|
|
else if (s == "gear")
|
|
{
|
|
intValue = target->equipment;
|
|
}
|
|
else if (s == "colors")
|
|
{
|
|
intValue = target->countColors();
|
|
}
|
|
else if (s == "auras")
|
|
{
|
|
intValue = target->auras;
|
|
}
|
|
else if (s == "manacost")
|
|
{
|
|
if (target->currentZone == target->controller()->game->stack)//X is 0 except if it's on the stack
|
|
intValue = target->myconvertedcost + target->castX;
|
|
else
|
|
intValue = target->myconvertedcost;
|
|
}
|
|
else if (s == "azorius")//devotion blue white
|
|
{
|
|
intValue = countDevotionTo(card,card->controller()->inPlay(),Constants::MTG_COLOR_BLUE,Constants::MTG_COLOR_WHITE);
|
|
}
|
|
else if (s == "boros")//devotion red white
|
|
{
|
|
intValue = countDevotionTo(card,card->controller()->inPlay(),Constants::MTG_COLOR_WHITE,Constants::MTG_COLOR_RED);
|
|
}
|
|
else if (s == "dimir")//devotion blue black
|
|
{
|
|
intValue = countDevotionTo(card,card->controller()->inPlay(),Constants::MTG_COLOR_BLACK,Constants::MTG_COLOR_BLUE);
|
|
}
|
|
else if (s == "golgari")//devotion to green black
|
|
{
|
|
intValue = countDevotionTo(card,card->controller()->inPlay(),Constants::MTG_COLOR_BLACK,Constants::MTG_COLOR_GREEN);
|
|
}
|
|
else if (s == "gruul")//devotion to green red
|
|
{
|
|
intValue = countDevotionTo(card,card->controller()->inPlay(),Constants::MTG_COLOR_GREEN,Constants::MTG_COLOR_RED);
|
|
}
|
|
else if (s == "izzet")//devotion to red blue
|
|
{
|
|
intValue = countDevotionTo(card,card->controller()->inPlay(),Constants::MTG_COLOR_BLUE,Constants::MTG_COLOR_RED);
|
|
}
|
|
else if (s == "orzhov")//devotion to white black
|
|
{
|
|
intValue = countDevotionTo(card,card->controller()->inPlay(),Constants::MTG_COLOR_BLACK,Constants::MTG_COLOR_WHITE);
|
|
}
|
|
else if (s == "rakdos")//devotion to red black
|
|
{
|
|
intValue = countDevotionTo(card,card->controller()->inPlay(),Constants::MTG_COLOR_BLACK,Constants::MTG_COLOR_RED);
|
|
}
|
|
else if (s == "selesnya")//devotion to green white
|
|
{
|
|
intValue = countDevotionTo(card,card->controller()->inPlay(),Constants::MTG_COLOR_GREEN,Constants::MTG_COLOR_WHITE);
|
|
}
|
|
else if (s == "simic")//devotion to green blue
|
|
{
|
|
intValue = countDevotionTo(card,card->controller()->inPlay(),Constants::MTG_COLOR_BLUE,Constants::MTG_COLOR_GREEN);
|
|
}
|
|
else if (s == "Iroas")//devotion to red white
|
|
{
|
|
intValue = countDevotionTo(card, card->controller()->inPlay(), Constants::MTG_COLOR_RED, Constants::MTG_COLOR_WHITE);
|
|
}
|
|
else if (s.find("type:") != string::npos)
|
|
{
|
|
size_t begins = s.find("type:");
|
|
string theType = s.substr(begins + 5);
|
|
size_t zoned = theType.find(":");
|
|
if(zoned == string::npos)
|
|
{
|
|
theType.append("|mybattlefield");
|
|
}
|
|
else
|
|
{
|
|
replace(theType.begin(), theType.end(), ':', '|');
|
|
}
|
|
int color = 0;
|
|
if (theType.find("mana") != string::npos) {
|
|
color = ManaCost::parseManaSymbol(theType[4]);
|
|
theType.replace(0, 5, "*");
|
|
}
|
|
TargetChooserFactory tf(card->getObserver());
|
|
TargetChooser * tc = tf.createTargetChooser(theType.c_str(),NULL);
|
|
tc->other = other;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * p = card->getObserver()->players[i];
|
|
MTGGameZone * zones[] = { p->game->inPlay, p->game->graveyard, p->game->hand, p->game->library, p->game->exile };
|
|
for (int k = 0; k < 5; k++)
|
|
{
|
|
MTGGameZone * zone = zones[k];
|
|
if (tc->targetsZone(zone, card))
|
|
{
|
|
if (color)
|
|
{
|
|
intValue += zone->countTotalManaSymbols(tc, color);
|
|
}
|
|
else
|
|
{
|
|
intValue += zone->countByCanTarget(tc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(tc);
|
|
}
|
|
else if (s.find("restriction{") != string::npos)
|
|
{
|
|
vector<string> splitRest = parseBetween(s,"restriction{","}");
|
|
if (splitRest.size())
|
|
{
|
|
AbilityFactory abf(target->getObserver());
|
|
int checkCond = abf.parseCastRestrictions(target,target->controller(),splitRest[1].c_str());
|
|
if(checkCond)
|
|
intValue = 1;
|
|
}
|
|
}
|
|
else if (s.find("counter{") != string::npos)
|
|
{
|
|
intValue = 0;
|
|
vector<string>counterName = parseBetween(s,"counter{","}");
|
|
if(counterName.size())
|
|
{
|
|
AbilityFactory abf(target->getObserver());
|
|
Counter * counter = abf.parseCounter(counterName[1], NULL);
|
|
if(counter && target->counters && target->counters->hasCounter(counter->name.c_str(),counter->power,counter->toughness))
|
|
{
|
|
Counter * targetCounter = target->counters->hasCounter(counter->name.c_str(), counter->power, counter->toughness);
|
|
intValue = targetCounter->nb;
|
|
}
|
|
SAFE_DELETE(counter);
|
|
}
|
|
}
|
|
else if (s.find("convertedcost:") != string::npos || s.find("power:") != string::npos || s.find("toughness:") != string::npos)
|
|
{
|
|
bool powerCheck = false;
|
|
bool toughnessCheck = false;
|
|
bool costCheck = false;
|
|
intValue = 0;
|
|
vector<string>convertedType = parseBetween(s,"convertedcost:",":");
|
|
if(convertedType.size())
|
|
costCheck = true;
|
|
else
|
|
{
|
|
convertedType = parseBetween(s,"power:",":");
|
|
if(convertedType.size())
|
|
powerCheck = true;
|
|
else
|
|
{
|
|
convertedType = parseBetween(s,"toughness:",":");
|
|
if(convertedType.size())
|
|
toughnessCheck = true;
|
|
}
|
|
}
|
|
if(!convertedType.size())
|
|
return;
|
|
bool high = false;
|
|
int highest = 0;
|
|
int lowest = 5000;
|
|
if(convertedType[1].find("highest") != string::npos)
|
|
high = true;
|
|
|
|
string theType = convertedType[2];
|
|
size_t zoned = theType.find(":");
|
|
if(zoned == string::npos)
|
|
{
|
|
theType.append("|mybattlefield");
|
|
}
|
|
else
|
|
{
|
|
replace(theType.begin(), theType.end(), ':', '|');
|
|
}
|
|
TargetChooserFactory tf(card->getObserver());
|
|
TargetChooser * tc = tf.createTargetChooser(theType.c_str(),NULL);
|
|
tc->other = other;
|
|
int check = 0;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * p = card->getObserver()->players[i];
|
|
MTGGameZone * zones[] = { p->game->battlefield, p->game->graveyard, p->game->hand, p->game->library, p->game->exile };
|
|
for (int k = 0; k < 5; k++)
|
|
{
|
|
MTGGameZone * zone = zones[k];
|
|
if(tc->targetsZone(zone,target))
|
|
{
|
|
for(unsigned int w = 0;w < zone->cards.size();++w)
|
|
{
|
|
MTGCardInstance * cCard = zone->cards[w];
|
|
if(tc->canTarget(cCard))
|
|
{
|
|
if(costCheck)
|
|
check = cCard->getManaCost()->getConvertedCost();
|
|
if(powerCheck)
|
|
check = cCard->power;
|
|
if(toughnessCheck)
|
|
check = cCard->toughness;
|
|
|
|
if(check > highest)
|
|
highest = check;
|
|
if(check <= lowest)
|
|
lowest = check;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(lowest == 5000)
|
|
lowest = 0;
|
|
SAFE_DELETE(tc);
|
|
intValue = high?highest:lowest;
|
|
}
|
|
else if (s == "sunburst")
|
|
{
|
|
intValue = 0;
|
|
if (card && card->previous && card->previous->previous)
|
|
{
|
|
intValue = card->previous->previous->sunburst;
|
|
}
|
|
}
|
|
else if (s == "converge")
|
|
{
|
|
intValue = 0;
|
|
for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i)
|
|
{
|
|
if(card->getManaCost()->getManaUsedToCast()->hasColor(i))
|
|
intValue +=1;
|
|
}
|
|
}
|
|
else if (s == "countallspell")
|
|
{
|
|
intValue = card->controller()->game->stack->seenThisTurn("*", Constants::CAST_ALL) + card->controller()->opponent()->game->stack->seenThisTurn("*", Constants::CAST_ALL);
|
|
}
|
|
else if (s == "countmycrespell")
|
|
{
|
|
intValue = card->controller()->game->stack->seenThisTurn("creature", Constants::CAST_ALL);
|
|
}
|
|
else if (s == "countmynoncrespell")
|
|
{
|
|
intValue = card->controller()->game->stack->seenThisTurn("*[-creature]", Constants::CAST_ALL);
|
|
}
|
|
else if (s == "evictg")
|
|
{
|
|
intValue = card->imprintG;
|
|
}
|
|
else if (s == "evictu")
|
|
{
|
|
intValue = card->imprintU;
|
|
}
|
|
else if (s == "evictr")
|
|
{
|
|
intValue = card->imprintR;
|
|
}
|
|
else if (s == "evictb")
|
|
{
|
|
intValue = card->imprintB;
|
|
}
|
|
else if (s == "evictw")
|
|
{
|
|
intValue = card->imprintW;
|
|
}
|
|
else if (s == "commongreen")
|
|
{
|
|
intValue = mostCommonColor(Constants::MTG_COLOR_GREEN, card);
|
|
}
|
|
else if (s == "commonblue")
|
|
{
|
|
intValue = mostCommonColor(Constants::MTG_COLOR_BLUE, card);
|
|
}
|
|
else if (s == "commonred")
|
|
{
|
|
intValue = mostCommonColor(Constants::MTG_COLOR_RED, card);
|
|
}
|
|
else if (s == "commonblack")
|
|
{
|
|
intValue = mostCommonColor(Constants::MTG_COLOR_BLACK, card);
|
|
}
|
|
else if (s == "commonwhite")
|
|
{
|
|
intValue = mostCommonColor(Constants::MTG_COLOR_WHITE, card);
|
|
}
|
|
else if (s == "targetedcurses")
|
|
{
|
|
if(card->playerTarget)
|
|
intValue = card->playerTarget->curses.size();
|
|
}
|
|
else if (s == "oplifetotal")
|
|
{
|
|
intValue = target->controller()->opponent()->life;
|
|
}
|
|
else if (s == "lifetotal")
|
|
{
|
|
intValue = target->controller()->life;
|
|
}
|
|
else if (s == "startinglife")
|
|
{
|
|
intValue = target->controller()->initLife;
|
|
}
|
|
else if (s == "abundantlife")//current life is morethan or equal to starting life
|
|
{
|
|
intValue = 0;
|
|
if (target->controller()->life >= target->controller()->initLife)
|
|
intValue = 1;
|
|
}
|
|
else if (s == "plibrarycount")
|
|
{
|
|
intValue = 0;
|
|
if (target->controller()->game->library->nb_cards)
|
|
intValue = target->controller()->game->library->nb_cards;
|
|
}
|
|
else if (s == "olibrarycount")
|
|
{
|
|
intValue = 0;
|
|
if (target->controller()->opponent()->game->library->nb_cards)
|
|
intValue = target->controller()->opponent()->game->library->nb_cards;
|
|
}
|
|
else if (s == "highestlifetotal")
|
|
{
|
|
intValue = target->controller()->life <= target->controller()->opponent()->life? target->controller()->opponent()->life:target->controller()->life;
|
|
}
|
|
else if (s == "lowestlifetotal")
|
|
{
|
|
intValue = target->controller()->life <= target->controller()->opponent()->life? target->controller()->life:target->controller()->opponent()->life;
|
|
}
|
|
else if (s == "thatmuch")
|
|
{
|
|
//the value that much is a variable to be used with triggered abilities.
|
|
//ie:when ever you gain life, draw that many cards. when used in a trigger draw:thatmuch, will return the value
|
|
//that the triggered event stored in the card for "that much".
|
|
intValue = 0;
|
|
intValue = target->thatmuch;
|
|
int checkagain = 0;
|
|
if(target->hasSubtype(Subtypes::TYPE_AURA) || target->hasSubtype(Subtypes::TYPE_EQUIPMENT))
|
|
{
|
|
if(target->target)
|
|
{
|
|
checkagain = target->target->thatmuch;
|
|
}
|
|
}
|
|
if(checkagain > intValue)
|
|
intValue = checkagain;
|
|
if(card && card->thatmuch > intValue)
|
|
intValue = card->thatmuch;
|
|
}
|
|
else if (s == "oplifelost")
|
|
{
|
|
intValue = target->controller()->opponent()->lifeLostThisTurn;
|
|
}
|
|
else if (s == "lifelost")
|
|
{
|
|
intValue = target->controller()->lifeLostThisTurn;
|
|
}
|
|
else if (s == "oplifegain")
|
|
{
|
|
intValue = target->controller()->opponent()->lifeGainedThisTurn;
|
|
}
|
|
else if (s == "lifegain")
|
|
{
|
|
intValue = target->controller()->lifeGainedThisTurn;
|
|
}
|
|
else if (s == "pdcount")
|
|
{
|
|
intValue = target->controller()->damageCount;
|
|
}
|
|
else if (s == "odcount")
|
|
{
|
|
intValue = target->controller()->opponent()->damageCount;
|
|
}
|
|
else if (s == "playerpoisoncount")
|
|
{
|
|
intValue = target->controller()->poisonCount;
|
|
}
|
|
else if (s == "opponentpoisoncount")
|
|
{
|
|
intValue = target->controller()->opponent()->poisonCount;
|
|
}
|
|
else if (s == "opponentlifetotal")
|
|
{
|
|
intValue = target->controller()->opponent()->life;
|
|
}
|
|
else if (s == "pdrewcount")
|
|
{
|
|
intValue = target->controller()->drawCounter;
|
|
}
|
|
else if (s == "odrewcount")
|
|
{
|
|
intValue = target->controller()->opponent()->drawCounter;
|
|
}
|
|
else if (s == "epicactivated")
|
|
{
|
|
intValue = target->controller()->epic;
|
|
}
|
|
else if (s == "snowcount")
|
|
{//this is just to count the number of snow mana produced ... just for debugging purposes...
|
|
intValue = target->controller()->snowManaG + target->controller()->snowManaU +target->controller()->snowManaR + target->controller()->snowManaB + target->controller()->snowManaW + target->controller()->snowManaC;
|
|
}
|
|
else if (s == "p" || s == "power")
|
|
{
|
|
intValue = target->getCurrentPower();
|
|
}
|
|
else if (s == "t" || s == "toughness")
|
|
{
|
|
intValue = target->getCurrentToughness();
|
|
}
|
|
else if (s == "countedamount")
|
|
{
|
|
intValue = target->CountedObjects;
|
|
}
|
|
else if (s == "kicked")
|
|
{
|
|
intValue = target->kicked;
|
|
}
|
|
else if (s == "handsize")
|
|
{
|
|
intValue = target->controller()->handsize;
|
|
}
|
|
else if (s == "olandg")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_GREEN, target, target->controller()->opponent());
|
|
}
|
|
else if (s == "olandu")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_BLUE, target, target->controller()->opponent());
|
|
}
|
|
else if (s == "olandr")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_RED, target, target->controller()->opponent());
|
|
}
|
|
else if (s == "olandb")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_BLACK, target, target->controller()->opponent());
|
|
}
|
|
else if (s == "olandw")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_WHITE, target, target->controller()->opponent());
|
|
}
|
|
else if (s == "olandc")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_ARTIFACT, target, target->controller()->opponent()) +
|
|
countManaProducedby(Constants::MTG_COLOR_WASTE, target, target->controller()->opponent());
|
|
}
|
|
else if (s == "plandg")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_GREEN, target, target->controller());
|
|
}
|
|
else if (s == "plandu")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_BLUE, target, target->controller());
|
|
}
|
|
else if (s == "plandr")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_RED, target, target->controller());
|
|
}
|
|
else if (s == "plandb")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_BLACK, target, target->controller());
|
|
}
|
|
else if (s == "plandw")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_WHITE, target, target->controller());
|
|
}
|
|
else if (s == "plandc")
|
|
{
|
|
intValue = countManaProducedby(Constants::MTG_COLOR_ARTIFACT, target, target->controller()) +
|
|
countManaProducedby(Constants::MTG_COLOR_WASTE, target, target->controller());
|
|
}
|
|
else if (s == "cantargetmycre")// can target my creature
|
|
{
|
|
intValue = countCanTargetby("creature", card, card->controller());
|
|
}
|
|
else if (s == "cantargetoppocre")// can target opponent creature
|
|
{
|
|
intValue = countCanTargetby("creature", card, card->controller()->opponent());
|
|
}
|
|
else if (s == "cantargetcre")// can target any creature
|
|
{
|
|
intValue = countCanTargetby("creature", card, card->controller()) + countCanTargetby("creature", card, card->controller()->opponent());
|
|
}
|
|
else if (s == "controllerturn")//intvalue = 1 if its your turn this(variable{controllerturn})
|
|
{
|
|
intValue = 0;
|
|
if ( target->controller() == target->getObserver()->currentPlayer)
|
|
intValue = 1;
|
|
}
|
|
else if (s == "opponentturn")//intvalue = 1 if its your turn this(variable{opponentturn})
|
|
{
|
|
intValue = 0;
|
|
if ( target->controller()->opponent() == target->getObserver()->currentPlayer)
|
|
intValue = 1;
|
|
}
|
|
else if (s == "phandcount")
|
|
{
|
|
intValue = target->controller()->game->hand->nb_cards;
|
|
}
|
|
else if (s == "ohandcount")
|
|
{
|
|
intValue = target->controller()->opponent()->game->hand->nb_cards;
|
|
}
|
|
else if (s == "urzatron")//Urza lands
|
|
{
|
|
if(card->controller()->game->battlefield->hasAlias(4192) && card->controller()->game->battlefield->hasAlias(4193) && card->controller()->game->battlefield->hasAlias(4194))
|
|
intValue = 1;
|
|
else
|
|
intValue = 0;
|
|
}
|
|
else if (s == "worshipped")//Worship
|
|
{
|
|
if(card->controller()->game->battlefield->hasType("creature"))
|
|
intValue = card->controller()->life;
|
|
else
|
|
intValue = 0;
|
|
}
|
|
else if (s == "pancientooze")//Ancient Ooze
|
|
{
|
|
intValue = 0;
|
|
for (int j = card->controller()->game->inPlay->nb_cards - 1; j >= 0; --j)
|
|
if (card->controller()->game->inPlay->cards[j]->hasType(Subtypes::TYPE_CREATURE) && card->controller()->game->inPlay->cards[j] != card)
|
|
intValue += card->controller()->game->inPlay->cards[j]->getManaCost()->getConvertedCost();
|
|
}
|
|
else if (s == "pdauntless")//Dauntless Dourbark
|
|
{
|
|
MTGGameZone * checkZone = card->controller()->inPlay();
|
|
intValue =
|
|
countCardTypeinZone("forest",checkZone) +
|
|
countCardTypeinZone("treefolk",checkZone);
|
|
}
|
|
else if (s == "pbasiclandtypes")//Basic Land types
|
|
{
|
|
MTGGameZone * checkZone = card->controller()->inPlay();
|
|
intValue = //mtg rules 205.4c
|
|
cardHasTypeinZone("waste", checkZone) +
|
|
cardHasTypeinZone("forest", checkZone) +
|
|
cardHasTypeinZone("plains", checkZone) +
|
|
cardHasTypeinZone("swamp", checkZone) +
|
|
cardHasTypeinZone("island", checkZone) +
|
|
cardHasTypeinZone("mountain", checkZone) +
|
|
cardHasTypeinZone("snow-covered forest", checkZone) +
|
|
cardHasTypeinZone("snow-covered plains", checkZone) +
|
|
cardHasTypeinZone("snow-covered swamp", checkZone) +
|
|
cardHasTypeinZone("snow-covered island", checkZone) +
|
|
cardHasTypeinZone("snow-covered mountain", checkZone);
|
|
}
|
|
else if (s == "myname")//Name of the card you control
|
|
{
|
|
intValue = countCardNameinZone(card->name,card->controller()->inPlay());
|
|
}
|
|
else if (s == "allmyname")//Plague Rats and others
|
|
{
|
|
intValue = 0;
|
|
for (int i = 0; i < 2; i++)
|
|
intValue += countCardNameinZone(card->name,card->getObserver()->players[i]->game->battlefield);
|
|
}
|
|
else if (s == "pgbzombie")//Soulless One
|
|
{
|
|
intValue = 0;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
intValue += countCardTypeinZone("zombie",card->getObserver()->players[i]->game->graveyard);
|
|
intValue += countCardTypeinZone("zombie",card->getObserver()->players[i]->game->battlefield);
|
|
}
|
|
}
|
|
else if (s == "pginstantsorcery")//Spellheart Chimera
|
|
{
|
|
intValue = 0;
|
|
for (int j = card->controller()->game->graveyard->nb_cards - 1; j >= 0; --j)
|
|
{
|
|
if (card->controller()->game->graveyard->cards[j]->hasType(Subtypes::TYPE_INSTANT)
|
|
||card->controller()->game->graveyard->cards[j]->hasType(Subtypes::TYPE_SORCERY))
|
|
intValue += 1;
|
|
}
|
|
}
|
|
else if (s == "gravecardtypes")//Tarmogoyf
|
|
{
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
MTGGameZone * checkZone = card->getObserver()->players[i]->game->graveyard;
|
|
intValue =
|
|
cardHasTypeinZone("planeswalker",checkZone) +
|
|
cardHasTypeinZone("tribal",checkZone) +
|
|
cardHasTypeinZone("sorcery",checkZone) +
|
|
cardHasTypeinZone("land",checkZone) +
|
|
cardHasTypeinZone("instant",checkZone) +
|
|
cardHasTypeinZone("enchantment",checkZone) +
|
|
cardHasTypeinZone("creature",checkZone) +
|
|
cardHasTypeinZone("artifact",checkZone);
|
|
}
|
|
}
|
|
else if (s == "powertotalinplay")//Count Total Power of Creatures you control... Formidable
|
|
{
|
|
intValue = 0;
|
|
for (int j = card->controller()->game->inPlay->nb_cards - 1; j >= 0; --j)
|
|
{
|
|
if (card->controller()->game->inPlay->cards[j]->hasType(Subtypes::TYPE_CREATURE))
|
|
intValue += card->controller()->game->inPlay->cards[j]->power;
|
|
}
|
|
}
|
|
else if (s == "revealedp")
|
|
{
|
|
if (card->revealedLast)
|
|
intValue = card->revealedLast->power;
|
|
}
|
|
else if (s == "revealedt")
|
|
{
|
|
if (card->revealedLast)
|
|
intValue = card->revealedLast->toughness;
|
|
}
|
|
else if (s == "revealedmana")
|
|
{
|
|
if (card->revealedLast)
|
|
intValue = card->revealedLast->getManaCost()->getConvertedCost();
|
|
}
|
|
else if(!intValue)//found nothing, try parsing a atoi
|
|
{
|
|
intValue = atoi(s.c_str());
|
|
}
|
|
if (intValue > 0)//dont divide by 0 the rest are valid.
|
|
{
|
|
if (halfup)
|
|
{
|
|
if (intValue % 2 == 1)
|
|
intValue++;
|
|
intValue = intValue / 2;
|
|
}
|
|
if (halfdown)
|
|
intValue = intValue / 2;
|
|
}
|
|
if (twice)
|
|
intValue = intValue * 2;
|
|
if (thrice)
|
|
intValue = intValue * 3;
|
|
if (intValue < 0)
|
|
{
|
|
//we remove "-" at the start and are parsing for real values.
|
|
//if we ended up with a value less than 0, then we return just 0
|
|
intValue = 0;
|
|
}
|
|
|
|
intValue *= multiplier;
|
|
}
|
|
public:
|
|
|
|
int countDevotionTo(MTGCardInstance * card, MTGGameZone * zone, int color1, int color2)
|
|
{
|
|
int counthybrid = 0;
|
|
TargetChooserFactory dtf(card->getObserver());
|
|
TargetChooser * dtc = dtf.createTargetChooser("*",NULL);
|
|
if (dtc->targetsZone(zone, card))
|
|
{
|
|
counthybrid += zone->countDevotion(dtc, color1, color2);
|
|
}
|
|
SAFE_DELETE(dtc);
|
|
return counthybrid;
|
|
}
|
|
|
|
int countCardNameinZone(string name, MTGGameZone * zone)
|
|
{
|
|
int count = 0;
|
|
for( int i= 0; i < zone->nb_cards; i ++)
|
|
if(zone->cards[i]->name == name)
|
|
count += 1;
|
|
return count;
|
|
}
|
|
|
|
int countCardsInPlaybyColor(int color, GameObserver * observer)
|
|
{
|
|
int count = 0;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
for( int j= 0; j < observer->players[i]->inPlay()->nb_cards; j++)
|
|
if(observer->players[i]->inPlay()->cards[j]->hasColor(color))
|
|
count += 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int mostCommonColor(int color, MTGCardInstance * card)
|
|
{
|
|
int maxColor = 0;
|
|
vector<int> colors;
|
|
|
|
for(int i = 1; i < 6; i++)
|
|
colors.push_back( countCardsInPlaybyColor(i, card->getObserver()) );
|
|
|
|
for(int j = 0; j < 5; j++)
|
|
if ( colors[j] > maxColor )
|
|
maxColor = colors[j];
|
|
|
|
if (countCardsInPlaybyColor(color, card->getObserver()) >= maxColor && maxColor > 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int countCardTypeinZone(string type, MTGGameZone * zone)
|
|
{
|
|
int count = 0;
|
|
for( int i= 0; i < zone->nb_cards; i ++)
|
|
if(zone->cards[i]->hasType(type))
|
|
count += 1;
|
|
return count;
|
|
}
|
|
|
|
int cardHasTypeinZone(const char * type, MTGGameZone * zone)
|
|
{
|
|
int count = 0;
|
|
if(zone->hasType(type))
|
|
count = 1;
|
|
return count;
|
|
}
|
|
|
|
int countCanTargetby(string type, MTGCardInstance * card, Player * player)
|
|
{
|
|
int count = 0;
|
|
for (int j = player->game->battlefield->nb_cards - 1; j >= 0; --j)
|
|
{
|
|
if (player->game->battlefield->cards[j]->hasType(type) && !player->game->battlefield->cards[j]->protectedAgainst(card))
|
|
count += 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int countManaProducedby(int color, MTGCardInstance * target, Player * player)
|
|
{
|
|
int count = 0;
|
|
for (size_t i = 0; i < target->getObserver()->mLayers->actionLayer()->manaObjects.size(); i++)
|
|
{
|
|
if (dynamic_cast<AManaProducer*> (((MTGAbility *) target->getObserver()->mLayers->actionLayer()->manaObjects[i])) &&
|
|
(dynamic_cast<AManaProducer*> (((MTGAbility *) target->getObserver()->mLayers->actionLayer()->manaObjects[i])))->source->isLand() &&
|
|
(dynamic_cast<AManaProducer*> (((MTGAbility *) target->getObserver()->mLayers->actionLayer()->manaObjects[i])))->source->controller() == player &&
|
|
(dynamic_cast<AManaProducer*> (((MTGAbility *) target->getObserver()->mLayers->actionLayer()->manaObjects[i])))->output->hasColor(color))
|
|
count += 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
WParsedInt(int value = 0)
|
|
{
|
|
intValue = value;
|
|
}
|
|
|
|
WParsedInt(string s, Spell * spell, MTGCardInstance * card)
|
|
{
|
|
init(s, spell, card);
|
|
}
|
|
|
|
WParsedInt(string s, MTGCardInstance * card)
|
|
{
|
|
init(s, NULL, card);
|
|
}
|
|
|
|
int getValue()
|
|
{
|
|
return intValue;
|
|
}
|
|
};
|
|
|
|
class WParsedPT
|
|
{
|
|
public:
|
|
bool ok;
|
|
WParsedInt power, toughness;
|
|
|
|
WParsedPT(int p, int t)
|
|
{
|
|
power.intValue = p;
|
|
toughness.intValue = t;
|
|
ok = true;
|
|
}
|
|
|
|
WParsedPT(string s, Spell * spell, MTGCardInstance * card)
|
|
{
|
|
size_t found = s.find("/");
|
|
ok = false;
|
|
if (found != string::npos)
|
|
{
|
|
size_t end = s.find(" ", found);
|
|
if (end == string::npos) end = s.size();
|
|
size_t start = s.find_last_of(" ", found);
|
|
if (start == string::npos)
|
|
start = 0;
|
|
else
|
|
start++;
|
|
power = WParsedInt(s.substr(start, found - start), spell, card);
|
|
toughness = WParsedInt(s.substr(found + 1, end - found - 1), spell, card);
|
|
|
|
ok = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
//
|
|
//Triggers
|
|
//
|
|
|
|
|
|
// Triggers When a card gets added to a zone (@movedTo)
|
|
class TrCardAddedToZone: public Trigger
|
|
{
|
|
public:
|
|
TargetZoneChooser * toTcZone, *fromTcZone;
|
|
TargetChooser * toTcCard, *fromTcCard;
|
|
bool sourceUntapped;
|
|
bool isSuspended;
|
|
TrCardAddedToZone(GameObserver* observer, int id, MTGCardInstance * source, TargetZoneChooser * toTcZone, TargetChooser * toTcCard,
|
|
TargetZoneChooser * fromTcZone = NULL, TargetChooser * fromTcCard = NULL,bool once = false,bool sourceUntapped = false,bool isSuspended = false) :
|
|
Trigger(observer, id, source, once), toTcZone(toTcZone), fromTcZone(fromTcZone), toTcCard(toTcCard), fromTcCard(fromTcCard),sourceUntapped(sourceUntapped),isSuspended(isSuspended)
|
|
{
|
|
};
|
|
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
WEventZoneChange * e = dynamic_cast<WEventZoneChange*> (event);
|
|
if (!e) return 0;
|
|
if(sourceUntapped && source->isTapped() == 1)
|
|
return 0;
|
|
if(isSuspended && !source->suspended)
|
|
return 0;
|
|
if (!toTcZone->targetsZone(e->to)) return 0;
|
|
if (!toTcCard->canTarget(e->card)) return 0;
|
|
if (fromTcZone && !fromTcZone->targetsZone(e->from)) return 0;
|
|
if (fromTcCard && !fromTcCard->canTarget(e->card->previous)) return 0;
|
|
|
|
//Battlefield is a special case. We usually don't want to trigger when a card comes from battlefield to battlefield
|
|
// http://code.google.com/p/wagic/issues/detail?id=179
|
|
if ((e->from == game->players[0]->game->battlefield || e->from == game->players[1]->game->battlefield) && (e->to
|
|
== game->players[0]->game->battlefield || e->to == game->players[1]->game->battlefield))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
~TrCardAddedToZone()
|
|
{
|
|
SAFE_DELETE(toTcZone);
|
|
SAFE_DELETE(toTcCard);
|
|
SAFE_DELETE(fromTcZone);
|
|
SAFE_DELETE(fromTcCard);
|
|
}
|
|
|
|
TrCardAddedToZone * clone() const
|
|
{
|
|
return NEW TrCardAddedToZone(*this);
|
|
}
|
|
};
|
|
|
|
class TrCardTapped: public Trigger
|
|
{
|
|
public:
|
|
bool tap;
|
|
TrCardTapped(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc, bool tap = true, bool once = false) :
|
|
Trigger(observer, id, source, once, tc), tap(tap)
|
|
{
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
WEventCardTap * e = dynamic_cast<WEventCardTap *> (event);
|
|
if (!e) return 0;
|
|
if (e->noTrigger)
|
|
return 0;
|
|
if (e->before == e->after) return 0;
|
|
if (e->after != tap) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
TrCardTapped * clone() const
|
|
{
|
|
return NEW TrCardTapped(*this);
|
|
}
|
|
};
|
|
|
|
class TrCardTappedformana: public Trigger
|
|
{
|
|
public:
|
|
bool tap;
|
|
TrCardTappedformana(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc, bool tap = true, bool once = false) :
|
|
Trigger(observer, id, source, once, tc), tap(tap)
|
|
{
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
WEventCardTappedForMana * e = dynamic_cast<WEventCardTappedForMana *> (event);
|
|
if (!e) return 0;
|
|
if (e->before == e->after) return 0;
|
|
if (e->after != tap) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
TrCardTappedformana * clone() const
|
|
{
|
|
return NEW TrCardTappedformana(*this);
|
|
}
|
|
};
|
|
|
|
class TrCombatTrigger: public Trigger
|
|
{
|
|
public:
|
|
TargetChooser * fromTc;//from(card)
|
|
bool limitOnceATurn;//can activate one time per turn
|
|
int triggeredTurn;//the turn it last activated
|
|
bool sourceUntapped;
|
|
bool opponentPoisoned;
|
|
//trigger types
|
|
bool attackingTrigger;
|
|
bool attackedAloneTrigger;
|
|
bool notBlockedTrigger;
|
|
bool attackBlockedTrigger;
|
|
bool blockingTrigger;
|
|
TrCombatTrigger(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc,TargetChooser * fromTc = NULL,
|
|
bool once = false,bool limitOnceATurn = false,bool sourceUntapped = false,bool opponentPoisoned = false,
|
|
bool attackingTrigger = false,bool attackedAloneTrigger = false,bool notBlockedTrigger = false,bool attackBlockedTrigger = false,bool blockingTrigger = false) :
|
|
Trigger(observer, id, source,once, tc), fromTc(fromTc),limitOnceATurn(limitOnceATurn),sourceUntapped(sourceUntapped),opponentPoisoned(opponentPoisoned),
|
|
attackingTrigger(attackingTrigger),attackedAloneTrigger(attackedAloneTrigger),notBlockedTrigger(notBlockedTrigger),
|
|
attackBlockedTrigger(attackBlockedTrigger),blockingTrigger(blockingTrigger)
|
|
{
|
|
triggeredTurn = -1;
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
//general restrictions
|
|
if (opponentPoisoned && !source->controller()->opponent()->isPoisoned())
|
|
return 0;
|
|
if (sourceUntapped && source->isTapped() == 1)
|
|
return 0;
|
|
if (limitOnceATurn && triggeredTurn == game->turn)
|
|
return 0;
|
|
//the follow cases are not "else'd" on purpose, triggers which are conjoined such as
|
|
//"whenever this card attacks, or attacks and is not blocked, are supposed to gernerally
|
|
//trigger only once MTG rule 509.a-d, from either/or..not else'ing the statements and
|
|
//listing them in order allows just that, a return on an event before hitting the
|
|
//next trigger condiational.
|
|
//when triggers are not conjoined you can simply add another combat trigger to the card as normal.
|
|
//an attacking creature can not also be a blocking creature.
|
|
WEventCardAttacked * attacked = dynamic_cast<WEventCardAttacked *> (event);
|
|
//event when a card was declared an attacker.
|
|
if (attacked && attackingTrigger && !attacked->card->didblocked)
|
|
{
|
|
if (!attacked->card->didattacked)
|
|
return 0;
|
|
if (!tc->canTarget(attacked->card))
|
|
return 0;
|
|
return returnResult();
|
|
}
|
|
WEventCardAttackedAlone * attackedAlone = dynamic_cast<WEventCardAttackedAlone *> (event);
|
|
//event when a card was declared an attacker, and attacked alone.
|
|
if (attackedAlone && attackedAloneTrigger && !attackedAlone->card->didblocked)
|
|
{
|
|
if (!attackedAlone->card->didattacked)
|
|
return 0;
|
|
if (!tc->canTarget(attackedAlone->card))
|
|
return 0;
|
|
return returnResult();
|
|
}
|
|
WEventCardBlocked * blocked = dynamic_cast<WEventCardBlocked *> (event);
|
|
//event when a card was declared a blocker.
|
|
if (blocked && blockingTrigger && !blocked->card->didattacked)
|
|
{
|
|
if(!blocked->card->didblocked)
|
|
return 0;
|
|
if (fromTc && !fromTc->canTarget(blocked->opponent))
|
|
return 0;
|
|
if (!tc->canTarget(blocked->card))
|
|
return 0;
|
|
return returnResult();
|
|
}
|
|
WEventCardAttackedNotBlocked * notblocked = dynamic_cast<WEventCardAttackedNotBlocked *> (event);
|
|
//event when a card was declared an attacker, but the attack was not blocked.
|
|
if (notblocked && notBlockedTrigger && !notblocked->card->didblocked)
|
|
{
|
|
if (!notblocked->card->didattacked)
|
|
return 0;
|
|
if (notblocked->card->isBlocked())
|
|
return 0;
|
|
if (!tc->canTarget(notblocked->card))
|
|
return 0;
|
|
return returnResult();
|
|
}
|
|
WEventCardAttackedBlocked * attackblocked = dynamic_cast<WEventCardAttackedBlocked *> (event);
|
|
//event when a card was declared an attacker, then it became "blocked".
|
|
if (attackblocked && attackBlockedTrigger && !attackblocked->card->didblocked)
|
|
{
|
|
if (!attackblocked->card->didattacked)
|
|
return 0;
|
|
if (!attackblocked->card->isBlocked())
|
|
return 0;
|
|
if (fromTc && !fromTc->canTarget(attackblocked->opponent))
|
|
return 0;
|
|
if (!tc->canTarget(attackblocked->card))
|
|
return 0;
|
|
return returnResult();
|
|
}
|
|
//default return is 0 || not triggered.
|
|
return 0;
|
|
}
|
|
|
|
int returnResult()
|
|
{
|
|
triggeredTurn = game->turn;
|
|
return 1;
|
|
}
|
|
|
|
~TrCombatTrigger()
|
|
{
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
TrCombatTrigger * clone() const
|
|
{
|
|
return NEW TrCombatTrigger(*this);
|
|
}
|
|
};
|
|
|
|
class TrcardDrawn: public Trigger
|
|
{
|
|
public:
|
|
bool thiscontroller, thisopponent;
|
|
TrcardDrawn(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc,bool once = false, bool thiscontroller = false, bool thisopponent = false) :
|
|
Trigger(observer, id, source,once, tc),thiscontroller(thiscontroller),thisopponent(thisopponent)
|
|
{
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
WEventcardDraw * e = dynamic_cast<WEventcardDraw *> (event);
|
|
if (!e) return 0;
|
|
if (!tc->canTarget(e->player)) return 0;
|
|
if(thiscontroller)
|
|
if(e->player != source->controller())
|
|
return 0;
|
|
if(thisopponent)
|
|
if(e->player == source->controller())
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
TrcardDrawn * clone() const
|
|
{
|
|
return NEW TrcardDrawn(*this);
|
|
}
|
|
};
|
|
|
|
class TrCardSacrificed: public Trigger
|
|
{
|
|
public:
|
|
TrCardSacrificed(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc,bool once = false) :
|
|
Trigger(observer, id, source, once, tc)
|
|
{
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
WEventCardSacrifice * e = dynamic_cast<WEventCardSacrifice *> (event);
|
|
if (!e) return 0;
|
|
MTGCardInstance * check = e->cardAfter;
|
|
MTGGameZone * oldZone = e->cardAfter->currentZone;
|
|
//MTGGameZone * currZone = check->currentZone;
|
|
check->currentZone = check->previousZone;
|
|
if (check->next && (check->next->currentZone|| check->isToken))
|
|
{
|
|
check = e->cardAfter->next;
|
|
oldZone = e->cardAfter->next->currentZone;
|
|
check->currentZone = e->cardAfter->next->previousZone;
|
|
}
|
|
if (!tc->canTarget(check,true))
|
|
{
|
|
check->currentZone = oldZone;
|
|
return 0;
|
|
}
|
|
check->currentZone = oldZone;
|
|
return 1;
|
|
}
|
|
|
|
TrCardSacrificed * clone() const
|
|
{
|
|
return NEW TrCardSacrificed(*this);
|
|
}
|
|
};
|
|
|
|
class TrCardDiscarded: public Trigger
|
|
{
|
|
public:
|
|
bool cycledTrigger;
|
|
TrCardDiscarded(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc,bool once = false, bool cycledTrigger = false) :
|
|
Trigger(observer, id, source, once, tc),cycledTrigger(cycledTrigger)
|
|
{
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
MTGCardInstance * targetCard = NULL;
|
|
if(cycledTrigger)
|
|
{
|
|
WEventCardCycle * c = dynamic_cast<WEventCardCycle *> (event);
|
|
if (!c) return 0;
|
|
targetCard = c->card;
|
|
}
|
|
else
|
|
{
|
|
WEventCardDiscard * e = dynamic_cast<WEventCardDiscard *> (event);
|
|
if (!e) return 0;
|
|
targetCard = e->card;
|
|
}
|
|
if (!targetCard || !tc->canTarget(targetCard)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
TrCardDiscarded * clone() const
|
|
{
|
|
return NEW TrCardDiscarded(*this);
|
|
}
|
|
};
|
|
|
|
class TrDamaged: public Trigger
|
|
{
|
|
public:
|
|
TargetChooser * fromTc;
|
|
int type;//this allows damagenoncombat and combatdamage to share this trigger
|
|
bool sourceUntapped;
|
|
bool limitOnceATurn;
|
|
int triggeredTurn;
|
|
bool thiscontroller;
|
|
bool thisopponent;
|
|
TrDamaged(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL, int type = 0,bool sourceUntapped = false,bool limitOnceATurn = false,bool once = false, bool thiscontroller = false, bool thisopponent = false) :
|
|
Trigger(observer, id, source, once, tc), fromTc(fromTc), type(type) , sourceUntapped(sourceUntapped),limitOnceATurn(limitOnceATurn),thiscontroller(thiscontroller),thisopponent(thisopponent)
|
|
{
|
|
triggeredTurn = -1;
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
WEventDamage * e = dynamic_cast<WEventDamage *> (event);
|
|
if (!e) return 0;
|
|
if (limitOnceATurn && triggeredTurn == game->turn)
|
|
return 0;
|
|
if (sourceUntapped && source->isTapped() == 1)
|
|
return 0;
|
|
if (!tc->canTarget(e->damage->target)) return 0;
|
|
if (fromTc && !fromTc->canTarget(e->damage->source)) return 0;
|
|
if (type == 1 && e->damage->typeOfDamage != Damage::DAMAGE_COMBAT) return 0;
|
|
if (type == 2 && e->damage->typeOfDamage == Damage::DAMAGE_COMBAT) return 0;
|
|
if (e->damage->target->type_as_damageable == Damageable::DAMAGEABLE_PLAYER)
|
|
{
|
|
if(thiscontroller)
|
|
if(e->damage->target != (Damageable *)source->controller())
|
|
return 0;
|
|
if(thisopponent)
|
|
if(e->damage->target == (Damageable *)source->controller())
|
|
return 0;
|
|
}
|
|
e->damage->target->thatmuch = e->damage->damage;
|
|
e->damage->source->thatmuch = e->damage->damage;
|
|
this->source->thatmuch = e->damage->damage;
|
|
triggeredTurn = game->turn;
|
|
|
|
return 1;
|
|
}
|
|
|
|
~TrDamaged()
|
|
{
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
TrDamaged * clone() const
|
|
{
|
|
return NEW TrDamaged(*this);
|
|
}
|
|
};
|
|
|
|
class TrLifeGained: public Trigger
|
|
{
|
|
public:
|
|
TargetChooser * fromTc;
|
|
int type;//this allows damagenoncombat and combatdamage to share this trigger
|
|
bool sourceUntapped, thiscontroller, thisopponent;
|
|
TrLifeGained(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL, int type = 0,bool sourceUntapped = false,bool once = false, bool thiscontroller = false, bool thisopponent = false) :
|
|
Trigger(observer, id, source, once , tc), fromTc(fromTc), type(type) , sourceUntapped(sourceUntapped) , thiscontroller(thiscontroller) , thisopponent(thisopponent)
|
|
{
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
WEventLife * e = dynamic_cast<WEventLife *> (event);
|
|
if (!e) return 0;
|
|
if (sourceUntapped && source->isTapped() == 1)
|
|
return 0;
|
|
if (!tc->canTarget(e->player)) return 0;
|
|
if (fromTc && !fromTc->canTarget(e->player)) return 0;
|
|
if (type == 1 && (e->amount > 0)) return 0;
|
|
if (type == 0 && (e->amount < 0)) return 0;
|
|
if(thiscontroller)
|
|
if(e->player != source->controller())
|
|
return 0;
|
|
if(thisopponent)
|
|
if(e->player == source->controller())
|
|
return 0;
|
|
e->player->thatmuch = abs(e->amount);
|
|
this->source->thatmuch = abs(e->amount);
|
|
|
|
return 1;
|
|
}
|
|
|
|
~TrLifeGained()
|
|
{
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
TrLifeGained * clone() const
|
|
{
|
|
return NEW TrLifeGained(*this);
|
|
}
|
|
};
|
|
|
|
//vampire trigger
|
|
class TrVampired: public Trigger
|
|
{
|
|
public:
|
|
TargetChooser * fromTc;
|
|
TrVampired(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL,bool once = false) :
|
|
Trigger(observer, id, source, once, tc), fromTc(fromTc)
|
|
{
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
WEventVampire * vamp = dynamic_cast<WEventVampire*>(event);
|
|
if (!vamp)
|
|
return 0;
|
|
|
|
if(fromTc && !fromTc->canTarget(vamp->source))
|
|
return 0;
|
|
tc->setAllZones();
|
|
//creature that were "vampired" only happens in battlefield, and event sent when they hit a grave.
|
|
//setting allzones, as we don't care since we know the preexisting condiations cover the zones.
|
|
if(!tc->canTarget(vamp->victem))
|
|
return 0;
|
|
return 1;
|
|
|
|
}
|
|
|
|
~TrVampired()
|
|
{
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
TrVampired * clone() const
|
|
{
|
|
return NEW TrVampired(*this);
|
|
}
|
|
};
|
|
|
|
//targetted trigger
|
|
class TrTargeted: public Trigger
|
|
{
|
|
public:
|
|
TargetChooser * fromTc;
|
|
int type;
|
|
TrTargeted(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL, int type = 0,bool once = false) :
|
|
Trigger(observer, id, source, once, tc), fromTc(fromTc), type(type)
|
|
{
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
WEventTarget * e = dynamic_cast<WEventTarget *> (event);
|
|
if (!e) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
if (fromTc && !fromTc->canTarget(e->source)) return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
~TrTargeted()
|
|
{
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
TrTargeted * clone() const
|
|
{
|
|
return NEW TrTargeted(*this);
|
|
}
|
|
};
|
|
|
|
//counter trigger
|
|
class TrCounter: public Trigger
|
|
{
|
|
public:
|
|
Counter * counter;
|
|
int type;
|
|
TrCounter(GameObserver* observer, int id, MTGCardInstance * source, Counter * counter, TargetChooser * tc, int type = 0,bool once = false) :
|
|
Trigger(observer, id, source, once, tc),counter(counter), type(type)
|
|
{
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
WEventCounters * e = dynamic_cast<WEventCounters *> (event);
|
|
if (!e) return 0;
|
|
if (type == 0 && !e->removed) return 0;
|
|
if (type == 1 && !e->added) return 0;
|
|
if (!(e->power == counter->power && e->toughness == counter->toughness && e->name == counter->name)) return 0;
|
|
if (tc && !tc->canTarget(e->targetCard)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
~TrCounter()
|
|
{
|
|
SAFE_DELETE(counter);
|
|
}
|
|
|
|
TrCounter * clone() const
|
|
{
|
|
TrCounter * mClone = NEW TrCounter(*this);
|
|
mClone->counter = NEW Counter(*this->counter);
|
|
return mClone;
|
|
}
|
|
};
|
|
|
|
//Tutorial Messaging
|
|
class ATutorialMessage: public MTGAbility, public IconButtonsController
|
|
{
|
|
public:
|
|
string mMessage;
|
|
float mElapsed, mSH, mSW;
|
|
JTexture * mBgTex;
|
|
JQuad * mBg[9];
|
|
bool mUserCloseRequest, mDontShow;
|
|
bool mIsImage;
|
|
int mLimit;
|
|
|
|
ATutorialMessage(GameObserver* observer, MTGCardInstance * source, string message, int limit = 1);
|
|
|
|
void Update(float dt);
|
|
bool CheckUserInput(JButton key);
|
|
void Render();
|
|
string getOptionName();
|
|
int alreadyShown();
|
|
|
|
ATutorialMessage * clone() const;
|
|
~ATutorialMessage();
|
|
|
|
//JGuiListener Implementation
|
|
using JGuiObject::ButtonPressed;
|
|
void ButtonPressed(int controllerId, int controlId);
|
|
};
|
|
|
|
//counters
|
|
class AACounter: public ActivatedAbility
|
|
{
|
|
public:
|
|
string counterstring;
|
|
int nb;
|
|
int maxNb;
|
|
int power;
|
|
int toughness;
|
|
string name;
|
|
string menu;
|
|
|
|
AACounter(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target,string counterstring, const char * _name, int power, int toughness, int nb,int maxNb = 0,
|
|
ManaCost * cost = NULL);
|
|
|
|
int resolve();
|
|
const string getMenuText();
|
|
AACounter * clone() const;
|
|
};
|
|
|
|
//counters
|
|
class AARemoveAllCounter: public ActivatedAbility
|
|
{
|
|
public:
|
|
int nb;
|
|
int power;
|
|
int toughness;
|
|
string name;
|
|
string menu;
|
|
bool all;
|
|
|
|
AARemoveAllCounter(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, const char * _name, int power, int toughness, int nb,
|
|
bool all,ManaCost * cost = NULL);
|
|
|
|
int resolve();
|
|
const string getMenuText();
|
|
AARemoveAllCounter * clone() const;
|
|
};
|
|
|
|
|
|
class AAResetDamage: public ActivatedAbility
|
|
{
|
|
public:
|
|
AAResetDamage(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, ManaCost * cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAResetDamage * clone() const;
|
|
};
|
|
|
|
class AAFakeAbility: public ActivatedAbility
|
|
{
|
|
public:
|
|
string named;
|
|
AAFakeAbility(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target,string _newName, ManaCost * cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAFakeAbility * clone() const;
|
|
};
|
|
|
|
class AAEPIC: public ActivatedAbility
|
|
{
|
|
public:
|
|
string named;
|
|
bool FField;
|
|
AAEPIC(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target,string _newName, ManaCost * cost = NULL, bool ffield = false );
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAEPIC * clone() const;
|
|
};
|
|
|
|
class AAFizzler: public ActivatedAbility
|
|
{
|
|
public:
|
|
ActionStack::FizzleMode fizzleMode; // action to do after fizzling
|
|
|
|
AAFizzler(GameObserver* observer, int _id, MTGCardInstance * card, Spell * _target, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAFizzler* clone() const;
|
|
};
|
|
|
|
/*
|
|
Generic classes
|
|
*/
|
|
|
|
class ANewAffinity: public MTGAbility
|
|
{
|
|
public:
|
|
string tcString;
|
|
string manaString;
|
|
ANewAffinity(GameObserver* observer, int _id, MTGCardInstance * _source,string Tc = "", string mana ="");
|
|
void Update(float dt);
|
|
int testDestroy();
|
|
ANewAffinity * clone() const;
|
|
};
|
|
|
|
//if/ifnot Cond then EFFECT
|
|
class IfThenAbility: public InstantAbility
|
|
{
|
|
public:
|
|
MTGAbility * delayedAbility;
|
|
MTGAbility * delayedElseAbility;
|
|
int type;
|
|
string Cond;
|
|
IfThenAbility(GameObserver* observer, int _id,MTGAbility * delayedAbility = NULL,MTGAbility * delayedElseAbility = NULL, MTGCardInstance * _source=NULL, Targetable * target = NULL, int type = 1,string Cond = "");
|
|
int resolve();
|
|
const string getMenuText();
|
|
IfThenAbility * clone() const;
|
|
~IfThenAbility();
|
|
};
|
|
|
|
//MayAbility: May do ...
|
|
class MayAbility: public MTGAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int triggered;
|
|
bool must;
|
|
string Cond;
|
|
Player * previousInterrupter;
|
|
MTGAbility * mClone;
|
|
|
|
MayAbility(GameObserver* observer, int _id, MTGAbility * _ability, MTGCardInstance * _source, bool must = false, string restriction = "");
|
|
|
|
void Update(float dt);
|
|
|
|
const string getMenuText();
|
|
int testDestroy();
|
|
|
|
int isReactingToTargetClick(Targetable * card);
|
|
|
|
int reactToTargetClick(Targetable * object);
|
|
|
|
MayAbility * clone() const;
|
|
~MayAbility();
|
|
|
|
};
|
|
|
|
//MayAbility with custom menues.
|
|
class MenuAbility: public MayAbility
|
|
{
|
|
public:
|
|
int triggered;
|
|
bool removeMenu;
|
|
bool must;
|
|
bool processed;
|
|
MTGAbility * mClone;
|
|
ManaCost * toPay;
|
|
vector<MTGAbility*>abilities;
|
|
vector<ManaCost*>optionalCosts;
|
|
Player * who;
|
|
string newNameString;
|
|
MenuAbility(GameObserver* observer, int _id, Targetable * target, MTGCardInstance * _source, bool must = false, vector<MTGAbility*>abilities = vector<MTGAbility*>(),Player * who = NULL,string _newName = "");
|
|
bool CheckUserInput(JButton key);
|
|
void Update(float dt);
|
|
int resolve();
|
|
const string getMenuText();
|
|
int testDestroy();
|
|
int isReactingToTargetClick(Targetable * card);
|
|
int reactToTargetClick(Targetable * object);
|
|
int reactToChoiceClick(Targetable * object,int choice,int control);
|
|
int processAbility();
|
|
MenuAbility * clone() const;
|
|
~MenuAbility();
|
|
|
|
};
|
|
|
|
class AAProliferate: public ActivatedAbility
|
|
{
|
|
public:
|
|
AAProliferate(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target,ManaCost * cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAProliferate * clone() const;
|
|
~AAProliferate();
|
|
};
|
|
|
|
//MultiAbility : triggers several actions for a cost
|
|
class MultiAbility: public ActivatedAbility
|
|
{
|
|
public:
|
|
vector<MTGAbility *> abilities;
|
|
//Maintains abilities created by this instance, for cleanup
|
|
vector<MTGAbility *> clones;
|
|
|
|
MultiAbility(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost);
|
|
int Add(MTGAbility * ability);
|
|
int resolve();
|
|
int addToGame();
|
|
int destroy();
|
|
const string getMenuText();
|
|
MultiAbility * clone() const;
|
|
~MultiAbility();
|
|
};
|
|
|
|
//Generic Activated Ability
|
|
|
|
class GenericActivatedAbility: public ActivatedAbility, public NestedAbility
|
|
{
|
|
public:
|
|
MTGGameZone * activeZone;
|
|
string newName;
|
|
|
|
GenericActivatedAbility(GameObserver* observer, string newName,string castRestriction,int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, string limit = "",MTGAbility * sideEffects = NULL,string usesBeforeSideEffects = "",
|
|
int restrictions = 0, MTGGameZone * dest = NULL);
|
|
GenericActivatedAbility(const GenericActivatedAbility& other);
|
|
int resolve();
|
|
const string getMenuText();
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL);
|
|
void Update(float dt);
|
|
int testDestroy();
|
|
GenericActivatedAbility * clone() const;
|
|
~GenericActivatedAbility();
|
|
|
|
};
|
|
|
|
//place a card on the bottom of owners library
|
|
class AALibraryBottom: public ActivatedAbility
|
|
{
|
|
public:
|
|
AALibraryBottom(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AALibraryBottom * clone() const;
|
|
};
|
|
|
|
//Copier. ActivatedAbility
|
|
class AACopier: public ActivatedAbility
|
|
{
|
|
public:
|
|
AACopier(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AACopier * clone() const;
|
|
};
|
|
//phaseout
|
|
class AAPhaseOut: public ActivatedAbility
|
|
{
|
|
public:
|
|
AAPhaseOut(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAPhaseOut * clone() const;
|
|
};
|
|
//AAImprint
|
|
class AAImprint: public ActivatedAbility
|
|
{
|
|
public:
|
|
AAImprint(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAImprint * clone() const;
|
|
};
|
|
//cloning...this makes a token thats a copy of the target.
|
|
class AACloner: public ActivatedAbility
|
|
{
|
|
public:
|
|
int who;
|
|
string with;
|
|
string types;
|
|
list<int> awith;
|
|
list<int> colors;
|
|
list<int> typesToAdd;
|
|
|
|
AACloner(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost = NULL, int who = 0,
|
|
string abilitiesStringList = "",string typeslist = "");
|
|
int resolve();
|
|
const string getMenuText();
|
|
virtual ostream& toString(ostream& out) const;
|
|
AACloner * clone() const;
|
|
~AACloner();
|
|
};
|
|
|
|
// AAMover
|
|
class AAMover: public ActivatedAbility
|
|
{
|
|
public:
|
|
string destination;
|
|
MTGAbility * andAbility;
|
|
string named;
|
|
bool undying;
|
|
bool persist;
|
|
AAMover(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, string dest,string _name, ManaCost * _cost = NULL, bool undying = false, bool persist = false);
|
|
MTGGameZone * destinationZone(Targetable * target = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
const char * getMenuText(TargetChooser * fromTc);
|
|
AAMover * clone() const;
|
|
~AAMover();
|
|
};
|
|
|
|
// AARandomMover
|
|
class AARandomMover: public ActivatedAbility
|
|
{
|
|
public:
|
|
string abilityTC;
|
|
string fromZone;
|
|
string toZone;
|
|
AARandomMover(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, string tcs, string from, string to);
|
|
MTGGameZone * destinationZone(Targetable * target = NULL,string zone = "");
|
|
int resolve();
|
|
const string getMenuText();
|
|
AARandomMover * clone() const;
|
|
~AARandomMover();
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------------------------
|
|
|
|
class AABuryCard: public ActivatedAbility
|
|
{
|
|
public:
|
|
MTGAbility * andAbility;
|
|
string menu;
|
|
AABuryCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AABuryCard * clone() const;
|
|
~AABuryCard();
|
|
};
|
|
|
|
class AADestroyCard: public ActivatedAbility
|
|
{
|
|
public:
|
|
MTGAbility * andAbility;
|
|
AADestroyCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AADestroyCard * clone() const;
|
|
~AADestroyCard();
|
|
};
|
|
|
|
class AASacrificeCard: public ActivatedAbility
|
|
{
|
|
public:
|
|
MTGAbility * andAbility;
|
|
AASacrificeCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AASacrificeCard * clone() const;
|
|
~AASacrificeCard();
|
|
};
|
|
|
|
class AADiscardCard: public ActivatedAbility
|
|
{
|
|
public:
|
|
MTGAbility * andAbility;
|
|
AADiscardCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AADiscardCard * clone() const;
|
|
~AADiscardCard();
|
|
};
|
|
|
|
/* Generic Target Ability */
|
|
class GenericTargetAbility: public TargetAbility
|
|
{
|
|
|
|
public:
|
|
int limitPerTurn;
|
|
string limit;
|
|
int counters;
|
|
MTGGameZone * activeZone;
|
|
string newName;
|
|
MTGAbility * sideEffects;
|
|
string usesBeforeSideEffects;
|
|
string tcString;
|
|
|
|
GenericTargetAbility(GameObserver* observer, string newName, string castRestriction, int _id, MTGCardInstance * _source, TargetChooser * _tc, MTGAbility * a, ManaCost * _cost = NULL, string limit = "",MTGAbility * sideEffects = NULL,string usesBeforeSideEffects = "", int restrictions = 0, MTGGameZone * dest = NULL,string tcString ="");
|
|
const string getMenuText();
|
|
~GenericTargetAbility();
|
|
GenericTargetAbility * clone() const;
|
|
int resolve();
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL);
|
|
void Update(float dt);
|
|
int testDestroy();
|
|
|
|
};
|
|
|
|
//ninjutsu
|
|
|
|
class ANinja: public ActivatedAbility
|
|
{
|
|
public:
|
|
ANinja(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target) :
|
|
ActivatedAbility(observer, _id, card)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if(!_target)
|
|
return 0;
|
|
MTGCardInstance * copy = _target->controller()->game->putInZone(_target,_target->currentZone,
|
|
source->controller()->game->temp);
|
|
Spell * spell = NEW Spell(game, copy);
|
|
spell->resolve();
|
|
MTGCardInstance * newcard = spell->source;
|
|
newcard->summoningSickness = 0;
|
|
newcard->tap();
|
|
newcard->setAttacker(1);
|
|
delete spell;
|
|
return 1;
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return "Ninjutsu";
|
|
}
|
|
|
|
ANinja * clone() const
|
|
{
|
|
return NEW ANinja(*this);
|
|
}
|
|
};
|
|
|
|
//remove from combat
|
|
|
|
class ACombatRemoval: public ActivatedAbility
|
|
{
|
|
public:
|
|
ACombatRemoval(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target) :
|
|
ActivatedAbility(observer, _id, card)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
_target->initAttackersDefensers();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return "Remove From Combat";
|
|
}
|
|
|
|
ACombatRemoval * clone() const
|
|
{
|
|
return NEW ACombatRemoval(*this);
|
|
}
|
|
};
|
|
|
|
//Drawer, allows to draw a card for a cost:
|
|
|
|
class AADrawer: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
|
|
string nbcardsStr;
|
|
bool noReplace;
|
|
AADrawer(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost,string nbcardsStr, int who =
|
|
TargetChooser::UNSET,bool noReplace = false);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AADrawer * clone() const;
|
|
int getNumCards();
|
|
};
|
|
|
|
|
|
|
|
class ACastRestriction: public AbilityTP
|
|
{
|
|
public:
|
|
TargetChooser * restrictionsScope; //a minimalist TargetChooser object describing the cards impacted by the restriction (for example: lands)
|
|
WParsedInt *value; //"maxPerTurn" value
|
|
MaxPerTurnRestriction * existingRestriction; // a pointer to the restriction that is being modified or that has been created (for restriction deletion purpose)
|
|
bool modifyExisting; //if set to true, means we want to modify an existing restriction, otherwise we create a new one
|
|
int zoneId; // identifier of the zone id impacted by the restriction
|
|
Player * targetPlayer; // Reference to the player impacted by the restriction (for restriction deletion purpose)
|
|
|
|
ACastRestriction(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who = TargetChooser::UNSET);
|
|
int addToGame();
|
|
int destroy();
|
|
const string getMenuText();
|
|
ACastRestriction * clone() const;
|
|
~ACastRestriction();
|
|
|
|
};
|
|
|
|
|
|
class AInstantCastRestrictionUEOT: public InstantAbilityTP
|
|
{
|
|
public:
|
|
ACastRestriction * ability;
|
|
|
|
|
|
AInstantCastRestrictionUEOT(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, TargetChooser * _restrictionsScope, WParsedInt * _value, bool _modifyExisting, int _zoneId, int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AInstantCastRestrictionUEOT * clone() const;
|
|
~AInstantCastRestrictionUEOT();
|
|
};
|
|
|
|
/*Gives life to target controller*/
|
|
class AALifer: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
string life_s;
|
|
AALifer(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string life_s, ManaCost * _cost = NULL,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AALifer * clone() const;
|
|
int getLife();
|
|
|
|
};
|
|
|
|
/*Player Wins Game*/
|
|
class AAWinGame: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
AAWinGame(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int who =
|
|
TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAWinGame * clone() const;
|
|
};
|
|
|
|
/*Changes one of the basic abilities of target
|
|
source : spell
|
|
target : spell target (creature)
|
|
modifier : 1 to add the ability, 0 to remove it
|
|
_ability : Id of the ability, as described in mtgdefinitions
|
|
*/
|
|
class ABasicAbilityModifier : public MTGAbility
|
|
{
|
|
public:
|
|
int modifier;
|
|
int ability;
|
|
bool value_before_modification;
|
|
ABasicAbilityModifier(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _ability, int _modifier = 1) :
|
|
MTGAbility(observer, _id, _source, _target), modifier(_modifier), ability(_ability)
|
|
{
|
|
aType = MTGAbility::STANDARDABILITYGRANT;
|
|
abilitygranted = ability;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
value_before_modification = ((MTGCardInstance *) target)->basicAbilities.test(ability);
|
|
|
|
assert(modifier < 2);
|
|
((MTGCardInstance *) target)->basicAbilities.set(ability, modifier > 0);
|
|
//---add or subtract so we can keep track - for future use
|
|
((MTGCardInstance *) target)->modbasicAbilities[ability] += modifier;
|
|
//---make sure no negative values
|
|
if(((MTGCardInstance *) target)->modbasicAbilities[ability] < 0)
|
|
((MTGCardInstance *) target)->modbasicAbilities[ability] = 0;
|
|
//---end add or subtract abilities
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
assert(modifier < 2);
|
|
((MTGCardInstance *) target)->basicAbilities.set(ability, value_before_modification);
|
|
|
|
return 1;
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return Constants::MTGBasicAbilities[ability];
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ABasicAbilityModifier ::: modifier : " << modifier << " ; ability : " << ability
|
|
<< " ; value_before_modification : " << value_before_modification << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
|
|
ABasicAbilityModifier * clone() const
|
|
{
|
|
return NEW ABasicAbilityModifier(*this);
|
|
}
|
|
};
|
|
|
|
/*Instants that modifies a basic ability until end of turn */
|
|
class AInstantBasicAbilityModifierUntilEOT : public InstantAbility
|
|
{
|
|
public:
|
|
bool stateBeforeActivation;
|
|
int ability;
|
|
int value;
|
|
AInstantBasicAbilityModifierUntilEOT(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _ability, int value)
|
|
: InstantAbility(observer, _id, _source, _target), ability(_ability), value(value)
|
|
{
|
|
aType = MTGAbility::STANDARDABILITYGRANT;
|
|
abilitygranted = ability;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
stateBeforeActivation = _target->basicAbilities.test(ability);
|
|
|
|
assert(value < 2);
|
|
_target->basicAbilities.set(ability, value > 0);
|
|
|
|
return InstantAbility::addToGame();
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return Constants::MTGBasicAbilities[ability];
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
_target->basicAbilities.set(ability, stateBeforeActivation);
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ABasicAbilityModifierUntilEOT ::: stateBeforeActivation : " << stateBeforeActivation << " ability : " << ability
|
|
<< " (";
|
|
return InstantAbility::toString(out) << ")";
|
|
}
|
|
|
|
AInstantBasicAbilityModifierUntilEOT * clone() const
|
|
{
|
|
return NEW AInstantBasicAbilityModifierUntilEOT(*this);
|
|
}
|
|
};
|
|
|
|
//Alteration of Ability until of turn (Aura)
|
|
class ABasicAbilityAuraModifierUntilEOT: public ActivatedAbility
|
|
{
|
|
public:
|
|
AInstantBasicAbilityModifierUntilEOT * ability;
|
|
ABasicAbilityAuraModifierUntilEOT(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost,
|
|
int _ability, int _value = 1) :
|
|
ActivatedAbility(observer, _id, _source, _cost, 0)
|
|
{
|
|
target = _target;
|
|
ability = NEW AInstantBasicAbilityModifierUntilEOT(observer, _id, _source, _target, _ability, _value);
|
|
aType = MTGAbility::STANDARDABILITYGRANT;
|
|
abilitygranted = _ability;
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance *, ManaCost *)
|
|
{
|
|
//The upper level "GenericTargetAbility" takes care of the click so we always return 0 here
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGAbility * a = ability->clone();
|
|
a->target = target;
|
|
a->addToGame();
|
|
return 1;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
resolve();
|
|
return ActivatedAbility::addToGame();
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
ABasicAbilityAuraModifierUntilEOT * clone() const
|
|
{
|
|
ABasicAbilityAuraModifierUntilEOT * a = NEW ABasicAbilityAuraModifierUntilEOT(*this);
|
|
a->ability = ability->clone();
|
|
return a;
|
|
}
|
|
|
|
~ABasicAbilityAuraModifierUntilEOT()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
};
|
|
|
|
/*Gives life each time a spell matching CardDescriptor's criteria are match . Optionnal manacost*/
|
|
class ASpellCastLife: public MTGAbility
|
|
{
|
|
public:
|
|
CardDescriptor trigger;
|
|
ManaCost * cost;
|
|
int life;
|
|
MTGCardInstance * lastUsedOn;
|
|
MTGCardInstance * lastChecked;
|
|
ASpellCastLife(GameObserver* observer, int id, MTGCardInstance * _source, CardDescriptor _trigger, ManaCost * _cost, int _life) :
|
|
MTGAbility(observer, id, _source), trigger(_trigger), cost(_cost), life(_life), lastUsedOn(NULL), lastChecked(NULL)
|
|
{
|
|
aType = MTGAbility::LIFER;
|
|
}
|
|
ASpellCastLife(GameObserver* observer, int id, MTGCardInstance * _source, int color, ManaCost * _cost, int _life) :
|
|
MTGAbility(observer, id, _source), cost(_cost), life(_life), lastUsedOn(NULL), lastChecked(NULL)
|
|
{
|
|
trigger.setColor(color);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card, ManaCost * = NULL)
|
|
{
|
|
if (_card == source && game->currentlyActing()->game->inPlay->hasCard(source))
|
|
{
|
|
if (game->currentlyActing()->getManaPool()->canAfford(cost))
|
|
{
|
|
Interruptible * laststackitem = game->mLayers->stackLayer()->getAt(-1);
|
|
if (laststackitem && laststackitem->type == ACTION_SPELL)
|
|
{
|
|
Spell * spell = (Spell*) laststackitem;
|
|
if (spell->source != lastUsedOn && trigger.match(spell->source))
|
|
{
|
|
lastChecked = spell->source;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card)
|
|
{
|
|
if (!isReactingToClick(_card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(cost);
|
|
if(!game->currentlyActing()->inPlay()->hasAbility(Constants::CANTCHANGELIFE))
|
|
game->currentlyActing()->life += life;
|
|
lastUsedOn = lastChecked;
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ASpellCastLife ::: trigger : ? " // << trigger
|
|
<< " ; cost : " << cost << " ; life : " << life << " ; lastUsedOn : " << lastUsedOn << " ; lastChecked : "
|
|
<< lastChecked << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
|
|
ASpellCastLife * clone() const
|
|
{
|
|
return NEW ASpellCastLife(*this);
|
|
}
|
|
|
|
~ASpellCastLife()
|
|
{
|
|
SAFE_DELETE(cost);
|
|
}
|
|
|
|
};
|
|
|
|
//Allows to untap at any moment for an amount of mana
|
|
class AUnBlocker: public MTGAbility
|
|
{
|
|
public:
|
|
ManaCost * cost;
|
|
AUnBlocker(GameObserver* observer, int id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
|
|
MTGAbility(observer, id, _source, _target), cost(_cost)
|
|
{
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card, ManaCost * = NULL)
|
|
{
|
|
if (_card == target && game->currentlyActing()->game->inPlay->hasCard(source) && _card->isTapped())
|
|
{
|
|
if (game->currentlyActing()->getManaPool()->canAfford(cost))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card)
|
|
{
|
|
if (!isReactingToClick(_card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(cost);
|
|
_card->attemptUntap();
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AUnBlocker ::: cost : " << cost << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
|
|
AUnBlocker * clone() const
|
|
{
|
|
return NEW AUnBlocker(*this);
|
|
}
|
|
|
|
};
|
|
|
|
//Protection From (creature/aura)
|
|
class AProtectionFrom: public MTGAbility
|
|
{
|
|
public:
|
|
TargetChooser * fromTc;
|
|
string tcstr;
|
|
AProtectionFrom(GameObserver* observer, int id, MTGCardInstance * _source, MTGCardInstance * _target, TargetChooser *fromTc,string tcstr) :
|
|
MTGAbility(observer, id, _source, _target), fromTc(fromTc),tcstr(tcstr)
|
|
{
|
|
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->addProtection(fromTc);
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
((MTGCardInstance *) target)->removeProtection(fromTc);
|
|
return 1;
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
sprintf(menuText,"Protection from %s",tcstr.c_str());
|
|
return menuText;
|
|
}
|
|
|
|
AProtectionFrom * clone() const
|
|
{
|
|
AProtectionFrom * a = NEW AProtectionFrom(*this);
|
|
a->fromTc = fromTc->clone();
|
|
return a;
|
|
}
|
|
|
|
~AProtectionFrom()
|
|
{
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
};
|
|
|
|
//cant be target of...
|
|
class ACantBeTargetFrom: public MTGAbility
|
|
{
|
|
public:
|
|
TargetChooser * fromTc;
|
|
ACantBeTargetFrom(GameObserver* observer, int id, MTGCardInstance * _source, MTGCardInstance * _target, TargetChooser *fromTc) :
|
|
MTGAbility(observer, id, _source, _target), fromTc(fromTc)
|
|
{
|
|
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->addCantBeTarget(fromTc);
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
((MTGCardInstance *) target)->removeCantBeTarget(fromTc);
|
|
return 1;
|
|
}
|
|
|
|
ACantBeTargetFrom * clone() const
|
|
{
|
|
ACantBeTargetFrom * a = NEW ACantBeTargetFrom(*this);
|
|
a->fromTc = fromTc->clone();
|
|
return a;
|
|
}
|
|
|
|
~ACantBeTargetFrom()
|
|
{
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
};
|
|
//Can't be blocked by...
|
|
class ACantBeBlockedBy: public MTGAbility
|
|
{
|
|
public:
|
|
TargetChooser * fromTc;
|
|
ACantBeBlockedBy(GameObserver* observer, int id, MTGCardInstance * _source, MTGCardInstance * _target, TargetChooser *fromTc) :
|
|
MTGAbility(observer, id, _source, _target), fromTc(fromTc)
|
|
{
|
|
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->addCantBeBlockedBy(fromTc);
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
((MTGCardInstance *) target)->removeCantBeBlockedBy(fromTc);
|
|
return 1;
|
|
}
|
|
|
|
ACantBeBlockedBy * clone() const
|
|
{
|
|
ACantBeBlockedBy * a = NEW ACantBeBlockedBy(*this);
|
|
a->fromTc = fromTc->clone();
|
|
return a;
|
|
}
|
|
|
|
~ACantBeBlockedBy()
|
|
{
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
};
|
|
//cant be the blocker of targetchooser
|
|
//Can't be blocked by...
|
|
class ACantBeBlockerOf: public MTGAbility
|
|
{
|
|
public:
|
|
TargetChooser * fromTc;
|
|
bool thisCard;
|
|
ACantBeBlockerOf(GameObserver* observer, int id, MTGCardInstance * _source, MTGCardInstance * _target, TargetChooser *fromTc,bool aThis) :
|
|
MTGAbility(observer, id, _source, _target), fromTc(fromTc),thisCard(aThis)
|
|
{
|
|
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if(!thisCard)
|
|
_target->addCantBeBlockerOf(fromTc);
|
|
else
|
|
_target->addCantBeBlockerOfCard((MTGCardInstance*)source);
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
if(!thisCard)
|
|
((MTGCardInstance *) target)->removeCantBeBlockerOf(fromTc);
|
|
else
|
|
((MTGCardInstance *) target)->removeCantBeBlockerOfCard((MTGCardInstance*)source);
|
|
return 1;
|
|
}
|
|
|
|
ACantBeBlockerOf * clone() const
|
|
{
|
|
ACantBeBlockerOf * a = NEW ACantBeBlockerOf(*this);
|
|
if(fromTc)
|
|
a->fromTc = fromTc->clone();
|
|
return a;
|
|
}
|
|
|
|
~ACantBeBlockerOf()
|
|
{
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
};
|
|
//Alteration of Power and Toughness (enchantments)
|
|
class APowerToughnessModifier: public MTGAbility
|
|
{
|
|
public:
|
|
WParsedPT * wppt;
|
|
string PT;
|
|
bool nonstatic;
|
|
bool cda;
|
|
APowerToughnessModifier(GameObserver* observer, int id, MTGCardInstance * _source, MTGCardInstance * _target, WParsedPT * wppt,string PT,bool nonstatic) :
|
|
MTGAbility(observer, id, _source, _target), wppt(wppt),PT(PT),nonstatic(nonstatic)
|
|
{
|
|
aType = MTGAbility::STANDARD_PUMP;
|
|
cda = PT.find("cdaactive") != string::npos;
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
if(!nonstatic)
|
|
return;
|
|
if(source->isToken && !source->isInPlay(game) && cda)
|
|
{
|
|
this->forceDestroy = 1;
|
|
return;
|
|
}
|
|
if(!cda || (cda && (((MTGCardInstance *) target)->isSettingBase < 1)))
|
|
{
|
|
if(((MTGCardInstance *) target)->isSwitchedPT)
|
|
((MTGCardInstance *) target)->switchPT(false);//revert
|
|
|
|
((MTGCardInstance *) target)->power -= wppt->power.getValue();
|
|
((MTGCardInstance *) target)->addToToughness(-wppt->toughness.getValue());
|
|
if(PT.size())
|
|
{
|
|
SAFE_DELETE(wppt);
|
|
if(cda)
|
|
wppt = NEW WParsedPT(cReplaceString(PT, " cdaactive", ""),NULL,(MTGCardInstance *) source);
|
|
else
|
|
wppt = NEW WParsedPT(cReplaceString(PT, " nonstatic", ""),NULL,(MTGCardInstance *) source);
|
|
}
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->power += wppt->power.getValue();
|
|
_target->addToToughness(wppt->toughness.getValue());
|
|
|
|
if(_target->isSwitchedPT)
|
|
_target->switchPT(true);//reaapply
|
|
}
|
|
if(cda)
|
|
{//update but not apply
|
|
if(PT.size())
|
|
{
|
|
SAFE_DELETE(wppt);
|
|
if(cda)
|
|
wppt = NEW WParsedPT(cReplaceString(PT, " cdaactive", ""),NULL,(MTGCardInstance *) source);
|
|
else
|
|
wppt = NEW WParsedPT(cReplaceString(PT, " nonstatic", ""),NULL,(MTGCardInstance *) source);
|
|
}
|
|
((MTGCardInstance *) target)->origpower = wppt->power.getValue();
|
|
((MTGCardInstance *) target)->origtoughness = (wppt->toughness.getValue() + ((MTGCardInstance *) target)->life)-((MTGCardInstance *) target)->life;//what?
|
|
}
|
|
}
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if(PT.size())
|
|
{
|
|
SAFE_DELETE(wppt);
|
|
if(cda)
|
|
wppt = NEW WParsedPT(cReplaceString(PT, " cdaactive", ""),NULL,(MTGCardInstance *) source);
|
|
else
|
|
wppt = NEW WParsedPT(cReplaceString(PT, " nonstatic", ""),NULL,(MTGCardInstance *) source);
|
|
}
|
|
if(cda)
|
|
{//Characteristic-defining abilities
|
|
if(_target->isSwitchedPT)
|
|
{
|
|
_target->switchPT(false);
|
|
_target->cdaPT(wppt->power.getValue(),wppt->toughness.getValue());
|
|
_target->switchPT(true);
|
|
}
|
|
else
|
|
_target->cdaPT(wppt->power.getValue(),wppt->toughness.getValue());
|
|
|
|
_target->isCDA = true;
|
|
}
|
|
else
|
|
{
|
|
if(_target->isSwitchedPT)
|
|
{
|
|
_target->switchPT(false);
|
|
_target->addptbonus(wppt->power.getValue(),wppt->toughness.getValue());
|
|
_target->switchPT(true);
|
|
}
|
|
else
|
|
_target->addptbonus(wppt->power.getValue(),wppt->toughness.getValue());
|
|
}
|
|
if(_target->has(Constants::INDESTRUCTIBLE) && wppt->toughness.getValue() < 0 && _target->toughness <= 0)
|
|
{
|
|
_target->controller()->game->putInGraveyard(_target);
|
|
}
|
|
return MTGAbility::addToGame();
|
|
}
|
|
int destroy()
|
|
{
|
|
if(cda)
|
|
{
|
|
/*??Do Nothing??*/;
|
|
}
|
|
else
|
|
{
|
|
((MTGCardInstance *) target)->removeptbonus(wppt->power.getValue(),wppt->toughness.getValue());
|
|
}
|
|
return 1;
|
|
}
|
|
const string getMenuText()
|
|
{
|
|
if(PT.size())
|
|
{
|
|
SAFE_DELETE(wppt);
|
|
if(cda)
|
|
wppt = NEW WParsedPT(cReplaceString(PT, " cdaactive", ""),NULL,(MTGCardInstance *) source);
|
|
else
|
|
wppt = NEW WParsedPT(cReplaceString(PT, " nonstatic", ""),NULL,(MTGCardInstance *) source);
|
|
}
|
|
sprintf(menuText, "%i/%i", wppt->power.getValue(), wppt->toughness.getValue());
|
|
return menuText;
|
|
}
|
|
APowerToughnessModifier * clone() const
|
|
{
|
|
APowerToughnessModifier * a = NEW APowerToughnessModifier(*this);
|
|
a->wppt = NEW WParsedPT(*(a->wppt));
|
|
return a;
|
|
}
|
|
~APowerToughnessModifier()
|
|
{
|
|
delete (wppt);
|
|
}
|
|
};
|
|
|
|
class GenericInstantAbility: public InstantAbility, public NestedAbility
|
|
{
|
|
public:
|
|
GenericInstantAbility(GameObserver* observer, int _id, MTGCardInstance * _source, Damageable * _target, MTGAbility * ability) :
|
|
InstantAbility(observer, _id, _source, _target), NestedAbility(ability)
|
|
{
|
|
ability->target = _target;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
ability->forceDestroy = -1;
|
|
ability->target = target; //Might have changed since initialization
|
|
ability->addToGame();
|
|
return InstantAbility::addToGame();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
if (game->removeObserver(ability))
|
|
ability = NULL;
|
|
else
|
|
SAFE_DELETE(ability);
|
|
return InstantAbility::destroy();
|
|
}
|
|
|
|
GenericInstantAbility * clone() const
|
|
{
|
|
GenericInstantAbility * a = NEW GenericInstantAbility(*this);
|
|
a->ability = ability->clone();
|
|
return a;
|
|
}
|
|
|
|
~GenericInstantAbility()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
};
|
|
|
|
//this generic ability assumes that what is added will take care of its own removel.
|
|
class GenericAbilityMod: public InstantAbility, public NestedAbility
|
|
{
|
|
public:
|
|
GenericAbilityMod(GameObserver* observer, int _id, MTGCardInstance * _source, Damageable * _target, MTGAbility * ability) :
|
|
InstantAbility(observer, _id, _source,_target), NestedAbility(ability)
|
|
{
|
|
ability->target = _target;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
InstantAbility::addToGame();
|
|
return 1;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGAbility * toAdd = ability->clone();
|
|
toAdd->forceDestroy = -1;
|
|
toAdd->target = target;
|
|
if(toAdd->getActionTc())
|
|
{
|
|
toAdd->reactToTargetClick(source);
|
|
return 1;
|
|
}
|
|
toAdd->addToGame();
|
|
return 1;
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
GenericAbilityMod * clone() const
|
|
{
|
|
GenericAbilityMod * a = NEW GenericAbilityMod(*this);
|
|
a->ability = ability->clone();
|
|
return a;
|
|
}
|
|
|
|
~GenericAbilityMod()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
};
|
|
//generic addtogame
|
|
class GenericAddToGame: public InstantAbility, public NestedAbility
|
|
{
|
|
public:
|
|
GenericAddToGame(GameObserver* observer, int _id, MTGCardInstance * _source, Damageable * _target, MTGAbility * ability) :
|
|
InstantAbility(observer, _id, _source,_target), NestedAbility(ability)
|
|
{
|
|
ability->target = _target;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
InstantAbility::addToGame();
|
|
return 1;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGAbility * toAdd = ability->clone();
|
|
toAdd->target = target;
|
|
if(toAdd->getActionTc())
|
|
return toAdd->reactToTargetClick(source);
|
|
return toAdd->addToGame();
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
GenericAddToGame * clone() const
|
|
{
|
|
GenericAddToGame * a = NEW GenericAddToGame(*this);
|
|
a->ability = ability->clone();
|
|
return a;
|
|
}
|
|
|
|
~GenericAddToGame()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
};
|
|
//Circle of Protections
|
|
class ACircleOfProtection: public TargetAbility
|
|
{
|
|
protected:
|
|
map<ReplacementEffect*, int> current;
|
|
public:
|
|
ACircleOfProtection(GameObserver* observer, int _id, MTGCardInstance * source, int _color) :
|
|
TargetAbility(observer, _id, source, NEW SpellOrPermanentTargetChooser(source->owner->getObserver(), source, _color), NEW ManaCost(), 0)
|
|
{
|
|
getCost()->add(Constants::MTG_COLOR_ARTIFACT, 1);
|
|
tc->targetter = NULL; //Circle of Protection doesn't use the word "source"
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _target = NULL;
|
|
if (!(_target = tc->getNextCardTarget()))
|
|
{
|
|
Spell * starget = tc->getNextSpellTarget();
|
|
_target = starget->source;
|
|
}
|
|
if (!_target) return 0;
|
|
REDamagePrevention * re = NEW REDamagePrevention(this, NEW CardTargetChooser(game, _target, NULL), NEW PlayerTargetChooser(game, 0, 1,
|
|
source->controller()));
|
|
current[re] = 1;
|
|
game->replacementEffects->add(re);
|
|
return 1;
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
for (map<ReplacementEffect*, int>::iterator it = current.begin(); it != current.end(); it++)
|
|
{
|
|
ReplacementEffect* re = (*it).first;
|
|
game->replacementEffects->remove(re);
|
|
delete re;
|
|
}
|
|
current.clear();
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP) clear();
|
|
TargetAbility::Update(dt);
|
|
}
|
|
|
|
~ACircleOfProtection()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ACircleOfProtection ::: (";
|
|
return TargetAbility::toString(out) << ")";
|
|
}
|
|
ACircleOfProtection * clone() const
|
|
{
|
|
return NEW ACircleOfProtection(*this);
|
|
}
|
|
};
|
|
|
|
//Basic regeneration mechanism for a Mana cost
|
|
class AStandardRegenerate: public ActivatedAbility
|
|
{
|
|
public:
|
|
AStandardRegenerate(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost = NULL) :
|
|
ActivatedAbility(observer, _id, _source, _cost, 0)
|
|
{
|
|
target = _target;
|
|
aType = MTGAbility::STANDARD_REGENERATE;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->regenerate();
|
|
return 1;
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return "Regenerate";
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AStandardRegenerate ::: (";
|
|
return ActivatedAbility::toString(out) << ")";
|
|
}
|
|
AStandardRegenerate * clone() const
|
|
{
|
|
return NEW AStandardRegenerate(*this);
|
|
}
|
|
};
|
|
|
|
//Aura Enchantments that provide controller of target life or damages at a given phase of their turn
|
|
class ARegularLifeModifierAura: public MTGAbility
|
|
{
|
|
public:
|
|
int life;
|
|
int phase;
|
|
int onlyIfTargetTapped;
|
|
ARegularLifeModifierAura(GameObserver* observer, int id, MTGCardInstance * _source, MTGCardInstance * _target, int _phase, int _life,
|
|
int _onlyIfTargetTapped = 0) :
|
|
MTGAbility(observer, id, _source, _target), life(_life), phase(_phase), onlyIfTargetTapped(_onlyIfTargetTapped)
|
|
{
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == phase && game->currentPlayer == ((MTGCardInstance *) target)->controller())
|
|
{
|
|
if ((!onlyIfTargetTapped || ((MTGCardInstance *) target)->isTapped()) && (!game->currentPlayer->inPlay()->hasAbility(Constants::CANTCHANGELIFE)))
|
|
{
|
|
if (life > 0)
|
|
{
|
|
game->currentPlayer->life += life;
|
|
}
|
|
else
|
|
{
|
|
game->mLayers->stackLayer()->addDamage(source, game->currentPlayer, -life);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ARegularLifeModifierAura ::: life : " << life << " ; phase : " << phase << " ; onlyIfTargetTapped : "
|
|
<< onlyIfTargetTapped << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
ARegularLifeModifierAura * clone() const
|
|
{
|
|
return NEW ARegularLifeModifierAura(*this);
|
|
}
|
|
};
|
|
//Generic Kird Ape
|
|
class AAsLongAs: public ListMaintainerAbility, public NestedAbility
|
|
{
|
|
public:
|
|
MTGAbility * a;
|
|
int includeSelf;
|
|
int mini, maxi;
|
|
bool miniFound, maxiFound, compareZone;
|
|
int amount[2];
|
|
AAsLongAs(GameObserver* observer, int _id, MTGCardInstance * _source, Damageable * _target, TargetChooser * _tc, int _includeSelf,
|
|
MTGAbility * ability, int mini = 0, int maxi = 0,bool miniFound = false,bool maxiFound = false,bool compareZone = false) :
|
|
ListMaintainerAbility(observer, _id, _source, _target), NestedAbility(ability), mini(mini), maxi(maxi),miniFound(miniFound),maxiFound(maxiFound),compareZone(compareZone)
|
|
{
|
|
for (int j = 0; j < 2; j++)
|
|
amount[j] = 0;
|
|
tc = _tc;
|
|
includeSelf = _includeSelf;
|
|
tc->targetter = NULL;
|
|
ability->source = source;
|
|
ability->target = target;
|
|
a = NULL;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
ListMaintainerAbility::Update(dt);
|
|
if(!ability->oneShot) {
|
|
SorterFunction();
|
|
}
|
|
}
|
|
|
|
void findMatchingAmount()
|
|
{
|
|
int Value = 0;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * p = game->players[i];
|
|
MTGGameZone * zones[] = { p->game->inPlay, p->game->graveyard, p->game->hand, p->game->library, p->game->exile };
|
|
for (int k = 0; k < 5; k++)
|
|
{
|
|
MTGGameZone * zone = zones[k];
|
|
Value = zone->countByCanTarget(tc);
|
|
}
|
|
amount[i] = Value;
|
|
}
|
|
}
|
|
|
|
int SorterFunction()
|
|
{
|
|
updateTargets();
|
|
int size = 0;
|
|
size = (int) cards.size();
|
|
if(compareZone)
|
|
findMatchingAmount();
|
|
//compare like tc zones to find a matching amount.
|
|
//if any player has less/more of Tc targetable cards, ability is valid.
|
|
/////////////////DO NOT REFACTOR THIS SECTION/////////////////////////////////////////
|
|
//these were seperated becuase previous methods were far too confusing to understand//
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
if (miniFound)
|
|
{
|
|
if (size > mini || (compareZone && (amount[0] > mini || amount[1] > mini)))
|
|
{
|
|
addAbilityToGame();
|
|
}
|
|
else
|
|
{
|
|
removeAbilityFromGame();
|
|
}
|
|
}
|
|
if (maxiFound)
|
|
{
|
|
if (size < maxi || (compareZone && (amount[0] < maxi || amount[1] < maxi)))
|
|
{
|
|
addAbilityToGame();
|
|
}
|
|
else
|
|
{
|
|
removeAbilityFromGame();
|
|
}
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////
|
|
cards.clear();
|
|
players.clear();
|
|
return 1;
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card)
|
|
{
|
|
if(card->isPhased || source->isPhased)
|
|
return 0;
|
|
if ((includeSelf || card != source) && tc->canTarget(card))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
//////////resolve is used when the aslongas is nested, or used on instants and sorceries/////////
|
|
int resolve()
|
|
{
|
|
SorterFunction();
|
|
if (ability->oneShot)
|
|
{
|
|
a = NULL; //allows to call the effect several times
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int addAbilityToGame()
|
|
{
|
|
if (a) return 0;
|
|
a = ability->clone();
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
SAFE_DELETE(a);
|
|
}
|
|
else
|
|
{
|
|
a->addToGame();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removeAbilityFromGame()
|
|
{
|
|
if (!a) return 0;
|
|
game->removeObserver(a);
|
|
a = NULL;
|
|
return 1;
|
|
}
|
|
/////////////////section required/////////////////////
|
|
int added(MTGCardInstance *)
|
|
{
|
|
return 1;
|
|
}
|
|
int added(Player *)
|
|
{
|
|
return 1;
|
|
}
|
|
int removed(MTGCardInstance *)
|
|
{
|
|
return 1;
|
|
}
|
|
///////////////////////////////////////////////////////
|
|
~AAsLongAs()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
if(ability)
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
else
|
|
{
|
|
return "Ability";
|
|
}
|
|
}
|
|
|
|
AAsLongAs * clone() const
|
|
{
|
|
AAsLongAs * a = NEW AAsLongAs(*this);
|
|
a->ability = ability->clone();
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Lords (Merfolk lord...) give power and toughness to OTHER creatures of their type, they can give them special abilities, regeneration
|
|
class ALord: public ListMaintainerAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int includeSelf;
|
|
map<Damageable *, MTGAbility *> abilities;
|
|
|
|
ALord(GameObserver* observer, int _id, MTGCardInstance * card, TargetChooser * _tc, int _includeSelf, MTGAbility * a) :
|
|
ListMaintainerAbility(observer, _id, card), NestedAbility(a)
|
|
{
|
|
tc = _tc;
|
|
tc->targetter = NULL;
|
|
includeSelf = _includeSelf;
|
|
if(ability->aType == MTGAbility::STANDARD_PREVENT)
|
|
aType = MTGAbility::STANDARD_PREVENT;
|
|
}
|
|
|
|
//returns true if it is me who created ability a attached to Damageable d
|
|
bool isParentOf(Damageable * d, MTGAbility * a)
|
|
{
|
|
if (abilities.find(d) != abilities.end())
|
|
return (abilities[d] == a);
|
|
return false;
|
|
}
|
|
|
|
int canBeInList(Player *p)
|
|
{
|
|
if (tc->canTarget(p)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card)
|
|
{
|
|
if(card->isPhased || source->isPhased)
|
|
{
|
|
removed(card);
|
|
return 0;
|
|
}
|
|
if ((includeSelf || card != source) && tc->canTarget(card))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
//TODO check if ability is oneShot ?
|
|
updateTargets();
|
|
cards.clear();
|
|
players.clear();
|
|
return 1;
|
|
}
|
|
|
|
int _added(Damageable * d)
|
|
{
|
|
MTGAbility * a = ability->clone();
|
|
a->target = d;
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
SAFE_DELETE(a);
|
|
}
|
|
else
|
|
{
|
|
if (d->type_as_damageable == Damageable::DAMAGEABLE_MTGCARDINSTANCE)
|
|
{
|
|
a->source = (MTGCardInstance *) d;
|
|
}
|
|
if (oneShot)
|
|
{
|
|
MTGAbility * wrapper = NEW GenericInstantAbility(game, 1, source, d, a);
|
|
wrapper->addToGame();
|
|
}
|
|
else
|
|
{
|
|
a->forcedAlive = 1;
|
|
a->addToGame();
|
|
abilities[d] = a;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int added(MTGCardInstance * card)
|
|
{
|
|
return _added(card);
|
|
}
|
|
|
|
int added(Player * p)
|
|
{
|
|
return _added(p);
|
|
}
|
|
|
|
int removed(MTGCardInstance * card)
|
|
{
|
|
if (abilities.find(card) != abilities.end()
|
|
&& !(forceDestroy == -1 && forcedAlive == 1)) //only embelms have forcedestroy = -1 and forcedalive = 1
|
|
{
|
|
abilities[card]->forcedAlive = 0;
|
|
game->removeObserver(abilities[card]);
|
|
abilities.erase(card);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
~ALord()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
//Special case for move
|
|
if (AAMover * move = dynamic_cast<AAMover *>(ability))
|
|
return move->getMenuText(tc);
|
|
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
ALord * clone() const
|
|
{
|
|
ALord * a = NEW ALord(*this);
|
|
a->ability = ability->clone();
|
|
return a;
|
|
}
|
|
};
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//a different lord for auras and enchantments. http://code.google.com/p/wagic/issues/detail?id=244
|
|
class ATeach: public ListMaintainerAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int includeSelf;
|
|
map<Damageable *, MTGAbility *> skills;
|
|
|
|
ATeach(GameObserver* observer, int _id, MTGCardInstance * card, TargetChooser * _tc, int, MTGAbility * a) :
|
|
ListMaintainerAbility(observer, _id, card), NestedAbility(a)
|
|
{
|
|
tc = _tc;
|
|
tc->targetter = NULL;
|
|
includeSelf = 0;
|
|
aType = MTGAbility::STANDARD_TEACH;
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card)
|
|
{
|
|
if(card->isPhased || source->isPhased)
|
|
return 0;
|
|
if(tc->canTarget(card) && card != tc->source)
|
|
{
|
|
if ((tc->source->hasSubtype(Subtypes::TYPE_AURA) || tc->source->hasSubtype(Subtypes::TYPE_EQUIPMENT) || tc->source->hasSubtype("instant")
|
|
|| tc->source->hasSubtype("sorcery")) && card == tc->source->target )
|
|
return 1;
|
|
if(tc->source->hasSubtype(Subtypes::TYPE_CREATURE))
|
|
{
|
|
for(size_t myChild = 0; myChild < tc->source->parentCards.size();++myChild)
|
|
{
|
|
if(tc->source->parentCards[myChild] == card)
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
updateTargets();
|
|
cards.clear();
|
|
players.clear();
|
|
return 1;
|
|
}
|
|
|
|
int added(MTGCardInstance * card)
|
|
{
|
|
return _added(card);
|
|
}
|
|
|
|
int removed(MTGCardInstance * card)
|
|
{
|
|
if (skills.find(card) != skills.end())
|
|
{
|
|
if(!game->removeObserver(skills[card]))
|
|
{
|
|
skills[card]->destroy();
|
|
}
|
|
if(skills[card])
|
|
skills.erase(card);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int _added(Damageable * d)
|
|
{
|
|
MTGAbility * a = ability->clone();
|
|
|
|
if (a->source->hasSubtype(Subtypes::TYPE_AURA) || a->source->hasSubtype(Subtypes::TYPE_EQUIPMENT) || a->source->hasSubtype("instant")
|
|
|| a->source->hasSubtype("sorcery"))
|
|
{
|
|
a->target = a->source->target;
|
|
}
|
|
else
|
|
{
|
|
if(tc->source->hasSubtype(Subtypes::TYPE_CREATURE))
|
|
a->target = d;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
SAFE_DELETE(a);
|
|
}
|
|
else
|
|
{
|
|
if (d->type_as_damageable == Damageable::DAMAGEABLE_MTGCARDINSTANCE)
|
|
{
|
|
a->source = (MTGCardInstance *) d;
|
|
}
|
|
if (oneShot)
|
|
{
|
|
MTGAbility * wrapper = NEW GenericInstantAbility(game, 1, source, d, a);
|
|
wrapper->addToGame();
|
|
}
|
|
else
|
|
{
|
|
skills[d] = a;
|
|
a->addToGame();
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
~ATeach()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
ATeach * clone() const
|
|
{
|
|
ATeach * a = NEW ATeach(*this);
|
|
a->ability = ability->clone();
|
|
return a;
|
|
}
|
|
|
|
};
|
|
//
|
|
|
|
/* assign a creature to block a target */
|
|
class AABlock: public InstantAbility
|
|
{
|
|
public:
|
|
AABlock(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
AABlock * clone() const;
|
|
};
|
|
|
|
/* assign a creature as a pair to target */
|
|
class PairCard: public InstantAbility
|
|
{
|
|
public:
|
|
PairCard(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
PairCard * clone() const;
|
|
};
|
|
//pick a card to dredge
|
|
class dredgeCard: public InstantAbility
|
|
{
|
|
public:
|
|
dredgeCard(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
dredgeCard * clone() const;
|
|
};
|
|
/* create a parent child association between cards */
|
|
class AAConnect: public InstantAbility
|
|
{
|
|
public:
|
|
AAConnect(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
AAConnect * clone() const;
|
|
};
|
|
|
|
//equipment
|
|
class AEquip: public TargetAbility
|
|
{
|
|
private:
|
|
bool isAttach;
|
|
vector<MTGAbility *> currentAbilities;
|
|
|
|
public:
|
|
AEquip(GameObserver* observer, int _id, MTGCardInstance * _source, ManaCost * _cost = NULL,
|
|
int restrictions = ActivatedAbility::AS_SORCERY);
|
|
|
|
int unequip();
|
|
int equip(MTGCardInstance * equipped);
|
|
|
|
int resolve();
|
|
const string getMenuText();
|
|
|
|
int testDestroy();
|
|
int destroy();
|
|
|
|
AEquip * clone() const;
|
|
};
|
|
|
|
class ATokenCreator: public ActivatedAbility
|
|
{
|
|
public:
|
|
list<int> abilities;
|
|
list<int> types;
|
|
list<int> colors;
|
|
int power, toughness;
|
|
int tokenId;
|
|
string _cardName;
|
|
string name;
|
|
string sabilities;
|
|
string starfound;
|
|
WParsedInt * multiplier;
|
|
int who;
|
|
bool aLivingWeapon;
|
|
string spt;
|
|
bool battleReady;
|
|
MTGCardInstance * myToken;
|
|
vector<MTGAbility *> currentAbilities;
|
|
MTGAbility * andAbility;
|
|
Player * tokenReciever;
|
|
//by id
|
|
ATokenCreator(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable *, ManaCost * _cost, int tokenId,string starfound, WParsedInt * multiplier = NULL,
|
|
int who = 0,bool aLivingWeapon = false) :
|
|
ActivatedAbility(observer, _id, _source, _cost, 0), tokenId(tokenId), starfound(starfound),multiplier(multiplier), who(who),aLivingWeapon(aLivingWeapon)
|
|
{
|
|
if (!multiplier) this->multiplier = NEW WParsedInt(1);
|
|
MTGCard * card = MTGCollection()->getCardById(tokenId);
|
|
if (card) name = card->data->getName();
|
|
battleReady = false;
|
|
andAbility = NULL;
|
|
}
|
|
//by name, card still require valid card.dat info, this just makes the primitive code far more readable. token(Eldrazi scion) instead of token(-1234234)...
|
|
ATokenCreator(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable *, ManaCost * _cost, string cardName, string starfound, WParsedInt * multiplier = NULL,
|
|
int who = 0, bool aLivingWeapon = false) :
|
|
ActivatedAbility(observer, _id, _source, _cost, 0), _cardName(cardName), starfound(starfound), multiplier(multiplier), who(who), aLivingWeapon(aLivingWeapon)
|
|
{
|
|
if (!multiplier) this->multiplier = NEW WParsedInt(1);
|
|
MTGCard * card = MTGCollection()->getCardByName(_cardName);
|
|
tokenId = card->getId();
|
|
if (card) name = card->data->getName();
|
|
battleReady = false;
|
|
andAbility = NULL;
|
|
}
|
|
//by construction
|
|
ATokenCreator(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable *, ManaCost * _cost, string sname, string stypes, int _power, int _toughness,
|
|
string sabilities, string starfound,WParsedInt * multiplier = NULL, int _who = 0,bool aLivingWeapon = false,string spt = "") :
|
|
ActivatedAbility(observer, _id, _source, _cost, 0),sabilities(sabilities),starfound(starfound), multiplier(multiplier), who(_who),aLivingWeapon(aLivingWeapon),spt(spt)
|
|
{
|
|
power = _power;
|
|
toughness = _toughness;
|
|
name = sname;
|
|
tokenId = 0;
|
|
aType = MTGAbility::STANDARD_TOKENCREATOR;
|
|
battleReady = false;
|
|
andAbility = NULL;
|
|
if (!multiplier) this->multiplier = NEW WParsedInt(1);
|
|
//TODO this is a copy/past of other code that's all around the place, everything should be in a dedicated parser class;
|
|
|
|
for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++)
|
|
{
|
|
size_t found = sabilities.find(Constants::MTGBasicAbilities[j]);
|
|
if (found != string::npos)
|
|
{
|
|
abilities.push_back(j);
|
|
}
|
|
}
|
|
|
|
if(sabilities.find("battleready") != string::npos)
|
|
battleReady = true;
|
|
|
|
if(sabilities.find("chosencolor") != string::npos)
|
|
{
|
|
colors.push_back(source->chooseacolor);
|
|
}
|
|
|
|
for (int j = 0; j < Constants::NB_Colors; j++)
|
|
{
|
|
size_t found = sabilities.find(Constants::MTGColorStrings[j]);
|
|
if (found != string::npos)
|
|
{
|
|
colors.push_back(j);
|
|
}
|
|
}
|
|
|
|
string s = stypes;
|
|
while (s.size())
|
|
{
|
|
size_t found = s.find(" ");
|
|
if (found != string::npos)
|
|
{
|
|
string toCheck = s.substr(0, found);
|
|
if(toCheck.find("chosentype") != string::npos || toCheck.find("Chosentype") != string::npos)
|
|
{
|
|
toCheck = source->chooseasubtype;
|
|
}
|
|
int id = MTGAllCards::findType(toCheck);
|
|
types.push_back(id);
|
|
s = s.substr(found + 1);
|
|
}
|
|
else
|
|
{
|
|
if(s.find("chosentype") != string::npos || s.find("Chosentype") != string::npos)
|
|
{
|
|
s = source->chooseasubtype;
|
|
}
|
|
int id = MTGAllCards::findType(s);
|
|
types.push_back(id);
|
|
s = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
if(!starfound.empty())
|
|
{
|
|
SAFE_DELETE(multiplier);
|
|
multiplier = NEW WParsedInt(starfound, NULL, (MTGCardInstance *)source);
|
|
}
|
|
if(!spt.empty())
|
|
{
|
|
vector<string> powertoughness = split( spt, '/');
|
|
WParsedInt * NewPow = NEW WParsedInt(powertoughness[0].c_str(),NULL,source);
|
|
WParsedInt * NewTou = NEW WParsedInt(powertoughness[1].c_str(),NULL,source);
|
|
power = NewPow->getValue();
|
|
toughness = NewTou->getValue();
|
|
SAFE_DELETE(NewPow);
|
|
SAFE_DELETE(NewTou);
|
|
}
|
|
for (int i = 0; i < Tokenizer(); ++i)
|
|
{
|
|
//MTGCardInstance * myToken;
|
|
if (tokenId)
|
|
{
|
|
MTGCard * card = MTGCollection()->getCardById(tokenId);
|
|
setTokenOwner();
|
|
myToken = NEW MTGCardInstance(card, tokenReciever->game);
|
|
}
|
|
else
|
|
{
|
|
myToken = NEW Token(name, source, power, toughness);
|
|
list<int>::iterator it;
|
|
for (it = types.begin(); it != types.end(); it++)
|
|
{
|
|
myToken->addType(*it);
|
|
}
|
|
for (it = colors.begin(); it != colors.end(); it++)
|
|
{
|
|
myToken->setColor(*it);
|
|
}
|
|
for (it = abilities.begin(); it != abilities.end(); it++)
|
|
{
|
|
myToken->basicAbilities[*it] = 1;
|
|
}
|
|
}
|
|
string tokenText = "";
|
|
if(sabilities.find("token(") == string::npos)
|
|
{
|
|
tokenText = "(";
|
|
size_t endAbility = sabilities.find(")");
|
|
string words = sabilities.substr(0,endAbility);
|
|
tokenText.append(words);
|
|
string sourcename = ((MTGCardInstance*)source)->name;
|
|
tokenText.append(") source: ");
|
|
tokenText.append( sourcename);
|
|
myToken->setText(tokenText);
|
|
}
|
|
setTokenOwner();
|
|
tokenReciever->game->temp->addCard(myToken);
|
|
Spell * spell = NEW Spell(game, myToken);
|
|
spell->resolve();
|
|
myToken = spell->source;
|
|
spell->source->owner = tokenReciever;
|
|
spell->source->isToken = 1;
|
|
spell->source->fresh = 1;
|
|
if(aLivingWeapon)
|
|
{
|
|
livingWeaponToken(spell->source);
|
|
}
|
|
if(battleReady)
|
|
{
|
|
battleReadyToken(spell->source);
|
|
}
|
|
//andability
|
|
if(andAbility)
|
|
{
|
|
//backup andAbility for copier and cloner
|
|
spell->source->TokenAndAbility = andAbility->clone();
|
|
MTGAbility * andAbilityClone = andAbility->clone();
|
|
andAbilityClone->target = spell->source;
|
|
if(andAbility->oneShot)
|
|
{
|
|
andAbilityClone->resolve();
|
|
SAFE_DELETE(andAbilityClone);
|
|
}
|
|
else
|
|
{
|
|
andAbilityClone->addToGame();
|
|
}
|
|
}
|
|
delete spell;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int Tokenizer()//tokenizer
|
|
{
|
|
int tokenize = 1;
|
|
if (source->controller()->game->battlefield->hasAbility(Constants::TOKENIZER))
|
|
{
|
|
int nbcards = source->controller()->game->battlefield->nb_cards;
|
|
for (int j = 0; j < nbcards; j++)
|
|
{
|
|
if (source->controller()->game->battlefield->cards[j]->has(Constants::TOKENIZER))
|
|
tokenize *= 2;
|
|
}
|
|
return multiplier->getValue()*tokenize;
|
|
}
|
|
else
|
|
return multiplier->getValue();
|
|
}
|
|
|
|
void setTokenOwner()
|
|
{
|
|
switch(who)
|
|
{
|
|
case TargetChooser::CONTROLLER:
|
|
tokenReciever = source->controller();
|
|
break;
|
|
case TargetChooser::OPPONENT:
|
|
tokenReciever = source->controller()->opponent();
|
|
break;
|
|
case TargetChooser::TARGET_CONTROLLER:
|
|
if(target)
|
|
{
|
|
tokenReciever = ((MTGCardInstance*)target)->controller();
|
|
break;
|
|
}
|
|
case TargetChooser::TARGETED_PLAYER:
|
|
{
|
|
tokenReciever = source->playerTarget;
|
|
break;
|
|
}
|
|
default:
|
|
tokenReciever = source->controller();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void livingWeaponToken(MTGCardInstance * card)
|
|
{
|
|
for (size_t i = 1; i < game->mLayers->actionLayer()->mObjects.size(); i++)
|
|
{
|
|
MTGAbility * a = ((MTGAbility *) game->mLayers->actionLayer()->mObjects[i]);
|
|
if (a->aType == MTGAbility::STANDARD_EQUIP && a->source == source)
|
|
{
|
|
AEquip* ae = dynamic_cast<AEquip*>(a);
|
|
ae->unequip();
|
|
ae->equip(card);
|
|
}
|
|
}
|
|
}
|
|
|
|
void battleReadyToken(MTGCardInstance * card)
|
|
{
|
|
card->summoningSickness = 0;
|
|
card->tap();
|
|
card->setAttacker(1);
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
sprintf(menuText, "Create %s", name.c_str());
|
|
return menuText;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ATokenCreator ::: abilities : ?" // << abilities
|
|
<< " ; types : ?" // << types
|
|
<< " ; colors : ?" // << colors
|
|
<< " ; power : " << power << " ; toughness : " << toughness << " ; name : " << name << " ; who : " << who << " (";
|
|
return ActivatedAbility::toString(out) << ")";
|
|
}
|
|
|
|
ATokenCreator * clone() const
|
|
{
|
|
ATokenCreator * a = NEW ATokenCreator(*this);
|
|
a->multiplier = NEW WParsedInt(*(multiplier));
|
|
return a;
|
|
}
|
|
|
|
~ATokenCreator()
|
|
{
|
|
SAFE_DELETE(multiplier);
|
|
}
|
|
|
|
};
|
|
|
|
//targetable abilities which are added to targeted players game.
|
|
class ATargetedAbilityCreator: public ActivatedAbility
|
|
{
|
|
public:
|
|
string name;
|
|
string sabilities;
|
|
int who;
|
|
MTGCardInstance * myDummy;
|
|
Player * abilityReciever;
|
|
ATargetedAbilityCreator(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable *, ManaCost * _cost,string _name, string abilityToAdd, int who = 0) :
|
|
ActivatedAbility(observer, _id, _source, _cost, 0),name(_name),sabilities(abilityToAdd), who(who)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
setAbilityOwner();
|
|
myDummy = NEW MTGCardInstance();
|
|
setAbilityOwner();
|
|
myDummy->setObserver(abilityReciever->getObserver());
|
|
myDummy->owner = abilityReciever;
|
|
myDummy->lastController = abilityReciever;
|
|
myDummy->storedSourceCard = source;
|
|
vector<string>magictextlines = split(sabilities,'_');
|
|
if(magictextlines.size())
|
|
{
|
|
string newMagicText = "";
|
|
for(unsigned int i = 0; i < magictextlines.size(); i++)
|
|
{
|
|
newMagicText.append(magictextlines[i]);
|
|
newMagicText.append("\n");
|
|
}
|
|
myDummy->magicText = newMagicText;
|
|
}
|
|
else
|
|
myDummy->magicText = sabilities;
|
|
abilityReciever->game->garbage->addCard(myDummy);
|
|
Spell * spell = NEW Spell(game, myDummy);
|
|
spell->resolve();
|
|
myDummy = spell->source;
|
|
spell->source->owner = abilityReciever;
|
|
delete spell;
|
|
return 1;
|
|
}
|
|
|
|
void setAbilityOwner()
|
|
{
|
|
switch(who)
|
|
{
|
|
case TargetChooser::CONTROLLER:
|
|
abilityReciever = source->controller();
|
|
break;
|
|
case TargetChooser::OPPONENT:
|
|
abilityReciever = source->controller()->opponent();
|
|
break;
|
|
case TargetChooser::TARGET_CONTROLLER:
|
|
if(target)
|
|
{
|
|
abilityReciever = ((MTGCardInstance*)target)->controller();
|
|
break;
|
|
}
|
|
case TargetChooser::TARGETED_PLAYER:
|
|
{
|
|
abilityReciever = source->playerTarget;
|
|
break;
|
|
}
|
|
default:
|
|
abilityReciever = source->controller()->opponent();
|
|
break;
|
|
}
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
if(name.size())
|
|
return name.c_str();
|
|
return "Ability";
|
|
}
|
|
|
|
ATargetedAbilityCreator * clone() const
|
|
{
|
|
ATargetedAbilityCreator * a = NEW ATargetedAbilityCreator(*this);
|
|
return a;
|
|
}
|
|
|
|
~ATargetedAbilityCreator()
|
|
{
|
|
}
|
|
|
|
};
|
|
///
|
|
//a paired lord
|
|
class APaired: public MTGAbility, public NestedAbility
|
|
{
|
|
public:
|
|
MTGAbility * a;
|
|
MTGAbility * b;
|
|
APaired(GameObserver* observer, int _id, MTGCardInstance * _source, Damageable * _target, MTGAbility * ability) :
|
|
MTGAbility(observer, _id, _source, _target), NestedAbility(ability)
|
|
{
|
|
ability->source = source;
|
|
ability->target = target;
|
|
a = NULL;
|
|
}
|
|
|
|
int removeFromGame()
|
|
{
|
|
return removeAbilityFromGame();
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
resolve();
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
if (source->myPair)
|
|
{
|
|
addAbilityToGame();
|
|
}
|
|
else
|
|
{
|
|
removeAbilityFromGame();
|
|
}
|
|
if (ability->oneShot) a = NULL; //allows to call the effect several times
|
|
return 1;
|
|
}
|
|
|
|
int addAbilityToGame()
|
|
{
|
|
if (a && b) return 0;
|
|
a = ability->clone();
|
|
b = ability->clone();
|
|
a->source = source;
|
|
a->target = source;
|
|
b->source = source->myPair;
|
|
b->target = source->myPair;
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
b->resolve();
|
|
SAFE_DELETE(a);
|
|
SAFE_DELETE(b);
|
|
}
|
|
else
|
|
{
|
|
a->addToGame();
|
|
b->addToGame();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
return removeAbilityFromGame();
|
|
}
|
|
|
|
int removeAbilityFromGame()
|
|
{
|
|
if (!a && !b) return 0;
|
|
game->removeObserver(a);
|
|
a = NULL;
|
|
game->removeObserver(b);
|
|
b = NULL;
|
|
return 1;
|
|
}
|
|
|
|
~APaired()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
APaired * clone() const
|
|
{
|
|
APaired * a = NEW APaired(*this);
|
|
a->ability = ability->clone();
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Foreach (plague rats...)
|
|
class AForeach: public ListMaintainerAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int includeSelf;
|
|
int mini;
|
|
int maxi;
|
|
map<Damageable *, MTGAbility *> abilities;
|
|
AForeach(GameObserver* observer, int _id, MTGCardInstance * card, Damageable * _target, TargetChooser * _tc, int _includeSelf, MTGAbility * a,
|
|
int mini = 0, int maxi = 0) :
|
|
ListMaintainerAbility(observer, _id, card, _target), NestedAbility(a), mini(mini), maxi(maxi)
|
|
{
|
|
tc = _tc;
|
|
tc->targetter = NULL;
|
|
includeSelf = _includeSelf;
|
|
ability->target = _target;
|
|
aType = MTGAbility::FOREACH;
|
|
naType = ability->aType;
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card)
|
|
{
|
|
if(card->isPhased || source->isPhased)
|
|
return 0;
|
|
if ((includeSelf || card != source) && tc->canTarget(card)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card)
|
|
{
|
|
if (mini && cards.size() <= (size_t) mini) return 0;
|
|
if (maxi && cards.size() >= (size_t) maxi) return 0;
|
|
|
|
MTGAbility * a = ability->clone();
|
|
a->target = target;
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
SAFE_DELETE(a);
|
|
}
|
|
else
|
|
{
|
|
a->addToGame();
|
|
abilities[card] = a;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card)
|
|
{
|
|
if (abilities.find(card) != abilities.end())
|
|
{
|
|
game->removeObserver(abilities[card]);
|
|
abilities.erase(card);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
AForeach * clone() const
|
|
{
|
|
AForeach * a = NEW AForeach(*this);
|
|
a->ability = ability->clone();
|
|
return a;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
//TODO check if ability is oneShot ?
|
|
updateTargets();
|
|
cards.clear();
|
|
players.clear();
|
|
return 1;
|
|
}
|
|
|
|
int checkActivation()
|
|
{
|
|
checkCards.clear();
|
|
checkTargets();
|
|
return checkCards.size();
|
|
}
|
|
|
|
~AForeach()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
};
|
|
|
|
class AThis: public MTGAbility, public NestedAbility
|
|
{
|
|
public:
|
|
MTGAbility * a;
|
|
ThisDescriptor * td;
|
|
string restrictionCheck;
|
|
AThis(GameObserver* observer, int _id, MTGCardInstance * _source, Damageable * _target, ThisDescriptor * _td, MTGAbility * ability, string restriction = "") :
|
|
MTGAbility(observer, _id, _source, _target), NestedAbility(ability)
|
|
{
|
|
td = _td;
|
|
restrictionCheck = restriction;
|
|
ability->source = source;
|
|
ability->target = target;
|
|
a = NULL;
|
|
SAFE_DELETE(tc);
|
|
}
|
|
|
|
int removeFromGame()
|
|
{
|
|
return removeAbilityFromGame();
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
resolve();
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
int match = 0;
|
|
if (td)
|
|
{
|
|
match = td->match(source);
|
|
}
|
|
else
|
|
{//restriction check instead of Targetchooser
|
|
AbilityFactory abf(target->getObserver());
|
|
int checkCond = abf.parseCastRestrictions(source, source->controller(), restrictionCheck);
|
|
if (checkCond)
|
|
match = 1;
|
|
}
|
|
if (match > 0)
|
|
{
|
|
addAbilityToGame();
|
|
}
|
|
else
|
|
{
|
|
removeAbilityFromGame();
|
|
}
|
|
if (ability->oneShot) a = NULL; //allows to call the effect several times
|
|
return 1;
|
|
}
|
|
|
|
int addAbilityToGame()
|
|
{
|
|
if (a) return 0;
|
|
a = ability->clone();
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
SAFE_DELETE(a);
|
|
}
|
|
else
|
|
{
|
|
a->addToGame();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removeAbilityFromGame()
|
|
{
|
|
if (!a) return 0;
|
|
game->removeObserver(a);
|
|
a = NULL;
|
|
return 1;
|
|
}
|
|
|
|
~AThis()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
SAFE_DELETE(td);
|
|
}
|
|
|
|
AThis * clone() const
|
|
{
|
|
AThis * a = NEW AThis(*this);
|
|
a->ability = ability->clone();
|
|
if(a->td)
|
|
a->td = td->clone();
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class AThisForEach: public MTGAbility, public NestedAbility
|
|
{
|
|
public:
|
|
ThisDescriptor * td;
|
|
vector<MTGAbility *> abilities;
|
|
AThisForEach(GameObserver* observer, int _id, MTGCardInstance * _source, Damageable * _target, ThisDescriptor * _td, MTGAbility * ability) :
|
|
MTGAbility(observer, _id, _source, _target), NestedAbility(ability)
|
|
{
|
|
td = _td;
|
|
ability->source = source;
|
|
ability->target = target;
|
|
SAFE_DELETE(tc);
|
|
aType = FOREACH;
|
|
}
|
|
|
|
int removeFromGame()
|
|
{
|
|
return removeAbilityFromGame();
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
resolve();
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
//TODO check if ability is oneShot ?
|
|
int matches;
|
|
matches = td->match(source);
|
|
if (matches > 0)
|
|
{
|
|
if (abilities.size() > (size_t)matches)
|
|
{
|
|
removeAbilityFromGame();
|
|
}
|
|
// i will equal abilities size, then we increment from there
|
|
//abilities size was previously being subtracted from matches
|
|
//tho since it was a nonstatic number, it would stop adding abilities prematurely.
|
|
for (size_t i = abilities.size(); i < (size_t)matches; i++)
|
|
{
|
|
addAbilityToGame();
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int addAbilityToGame()
|
|
{
|
|
MTGAbility * a = ability->clone();
|
|
a->target = target;
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
SAFE_DELETE(a);
|
|
}
|
|
else
|
|
{
|
|
a->addToGame();
|
|
abilities.push_back(a);
|
|
//abilities[abilities.size()] = a;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removeAbilityFromGame()
|
|
{
|
|
for (int i = abilities.size(); i > 0; i--)
|
|
{
|
|
game->removeObserver(abilities[i - 1]);
|
|
}
|
|
abilities.clear();
|
|
return 1;
|
|
}
|
|
|
|
~AThisForEach()
|
|
{
|
|
SAFE_DELETE(ability);
|
|
SAFE_DELETE(td);
|
|
|
|
if (abilities.size())
|
|
{
|
|
removeAbilityFromGame();
|
|
}
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
AThisForEach * clone() const
|
|
{
|
|
AThisForEach * a = NEW AThisForEach(*this);
|
|
a->ability = ability->clone();
|
|
a->td = td->clone();
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Modify Hand
|
|
class AModifyHand: public AbilityTP
|
|
{
|
|
public:
|
|
string hand;
|
|
AModifyHand(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, string hand, int who = TargetChooser::UNSET);
|
|
int addToGame();
|
|
int destroy();
|
|
const string getMenuText();
|
|
AModifyHand * clone() const;
|
|
//~AModifyHand();
|
|
|
|
};
|
|
|
|
//set a players hand size
|
|
class AASetHand: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
int hand;
|
|
|
|
AASetHand(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, int hand, ManaCost * _cost = NULL,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AASetHand * clone() const;
|
|
|
|
};
|
|
|
|
//lifeset
|
|
class AALifeSet: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
WParsedInt * life;
|
|
|
|
AALifeSet(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, WParsedInt * life, ManaCost * _cost = NULL,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AALifeSet * clone() const;
|
|
~AALifeSet();
|
|
|
|
};
|
|
|
|
//lifesetend
|
|
|
|
class AADamager: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
string d;
|
|
bool redirected;
|
|
|
|
AADamager(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, string d, ManaCost * _cost = NULL,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
int getDamage();
|
|
AADamager * clone() const;
|
|
|
|
};
|
|
|
|
//prevent next damage
|
|
class AADamagePrevent: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
int preventing;
|
|
|
|
AADamagePrevent(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, int preventing, ManaCost * _cost = NULL, int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AADamagePrevent * clone() const;
|
|
~AADamagePrevent();
|
|
};
|
|
|
|
//poison removel
|
|
class AAAlterPoison: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
int poison;
|
|
|
|
AAAlterPoison(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, int poison, ManaCost * _cost = NULL,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAAlterPoison * clone() const;
|
|
~AAAlterPoison();
|
|
};
|
|
/* Standard Damager, can choose a NEW target each time the price is paid */
|
|
class TADamager: public TargetAbility
|
|
{
|
|
public:
|
|
|
|
TADamager(GameObserver* observer, int id, MTGCardInstance * card, ManaCost * _cost, string d, TargetChooser * _tc = NULL) :
|
|
TargetAbility(observer, id, card, _tc, _cost, 0)
|
|
{
|
|
if (!tc) tc = NEW DamageableTargetChooser(game, card);
|
|
ability = NEW AADamager(game, id, card, NULL, d);
|
|
}
|
|
|
|
TADamager * clone() const
|
|
{
|
|
return NEW TADamager(*this);
|
|
}
|
|
};
|
|
//bestow
|
|
class ABestow : public ActivatedAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * _card;
|
|
ABestow(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
ABestow * clone() const;
|
|
};
|
|
|
|
/* Can tap a target for a cost */
|
|
class AATapper: public ActivatedAbility
|
|
{
|
|
public:
|
|
bool _sendNoEvent;
|
|
AATapper(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL, bool _sendNoEvent = true);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AATapper * clone() const;
|
|
};
|
|
|
|
/* Can untap a target for a cost */
|
|
class AAUntapper: public ActivatedAbility
|
|
{
|
|
public:
|
|
AAUntapper(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAUntapper * clone() const;
|
|
};
|
|
|
|
/*announce card X*/
|
|
class AAWhatsX : public ActivatedAbility
|
|
{
|
|
public:
|
|
int value;
|
|
MTGAbility * costRule;
|
|
AAWhatsX(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * source, int value = 0, MTGAbility * costRule = NULL);
|
|
int resolve();
|
|
const string getMenuText()
|
|
{
|
|
sprintf(menuText, "%i", value);
|
|
return menuText;
|
|
};
|
|
AAWhatsX * clone() const;
|
|
};
|
|
|
|
/* set max level up on a levelup creature this is an Ai hint ability, no effect for players.*/
|
|
class AAWhatsMax: public ActivatedAbility
|
|
{
|
|
public:
|
|
int value;
|
|
|
|
AAWhatsMax(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * source, ManaCost * _cost = NULL, int value = 0);
|
|
int resolve();
|
|
AAWhatsMax * clone() const;
|
|
};
|
|
//counts a targetchooser for use later by other effects
|
|
class AACountObject : public ActivatedAbility
|
|
{
|
|
public:
|
|
string value;
|
|
|
|
AACountObject(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * source, ManaCost * _cost = NULL, string value ="");
|
|
int resolve();
|
|
AACountObject * clone() const;
|
|
};
|
|
/* Can prevent a card from untapping next untap */
|
|
class AAFrozen: public ActivatedAbility
|
|
{
|
|
public:
|
|
bool freeze;
|
|
AAFrozen(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, bool tap, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAFrozen * clone() const;
|
|
};
|
|
/* ghetto new target*/
|
|
class AANewTarget: public ActivatedAbility
|
|
{
|
|
public:
|
|
bool retarget;
|
|
bool reequip;
|
|
bool newhook;
|
|
AANewTarget(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,bool retarget = false, ManaCost * _cost = NULL, bool reequip = false, bool newhook = false);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AANewTarget * clone() const;
|
|
};
|
|
/* morph*/
|
|
class AAMorph: public ActivatedAbility
|
|
{
|
|
public:
|
|
vector<MTGAbility *> currentAbilities;
|
|
AAMorph(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
int testDestroy();
|
|
const string getMenuText();
|
|
AAMorph * clone() const;
|
|
};
|
|
|
|
class AAMeldFrom : public ActivatedAbility
|
|
{
|
|
public:
|
|
string _MeldedName;
|
|
AAMeldFrom(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, string MeldedName = "");
|
|
int resolve();
|
|
|
|
const string getMenuText();
|
|
AAMeldFrom * clone() const;
|
|
};
|
|
/* meld*/
|
|
class AAMeld : public ActivatedAbility
|
|
{
|
|
public:
|
|
string _MeldedName;
|
|
AAMeld(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,string MeldedName = "");
|
|
int resolve();
|
|
|
|
const string getMenuText();
|
|
AAMeld * clone() const;
|
|
};
|
|
|
|
/* flip*/
|
|
class AAFlip: public InstantAbility
|
|
{
|
|
public:
|
|
vector<MTGAbility *> currentAbilities;
|
|
string flipStats;
|
|
bool isflipcard;
|
|
bool forcedcopy;
|
|
AAFlip(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,string flipStats, bool isflipcard = false, bool forcedcopy = false);
|
|
int resolve();
|
|
int testDestroy();
|
|
const string getMenuText();
|
|
AAFlip * clone() const;
|
|
};
|
|
/* dynamic ability build*/
|
|
class AADynamic: public ActivatedAbility
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
DYNAMIC_SOURCE_AMOUNT = 1,
|
|
DYNAMIC_MYTGT_AMOUNT = 2,
|
|
DYNAMIC_MYSELF_AMOUNT = 3,
|
|
DYNAMIC_MYFOE_AMOUNT = 4,
|
|
DYNAMIC_NB_AMOUNT = 5,
|
|
|
|
DYNAMIC_ABILITY_TYPE_POWER = 0,
|
|
DYNAMIC_ABILITY_TYPE_TOUGHNESS = 1,
|
|
DYNAMIC_ABILITY_TYPE_MANACOST = 2,
|
|
DYNAMIC_ABILITY_TYPE_COLORS = 3,
|
|
DYNAMIC_ABILITY_TYPE_AGE = 4,
|
|
DYNAMIC_ABILITY_TYPE_CHARGE = 5,
|
|
DYNAMIC_ABILITY_TYPE_ONEONECOUNTERS = 6,
|
|
DYNAMIC_ABILITY_TYPE_THATMUCH = 7,
|
|
DYNAMIC_ABILITY_TYPE_NB = 8,
|
|
|
|
DYNAMIC_ABILITY_EFFECT_STRIKE = 0,
|
|
DYNAMIC_ABILITY_EFFECT_DRAW = 1,
|
|
DYNAMIC_ABILITY_EFFECT_LIFEGAIN = 2,
|
|
DYNAMIC_ABILITY_EFFECT_PUMPPOWER = 3,
|
|
DYNAMIC_ABILITY_EFFECT_PUMPTOUGHNESS = 4,
|
|
DYNAMIC_ABILITY_EFFECT_PUMPBOTH = 5,
|
|
DYNAMIC_ABILITY_EFFECT_LIFELOSS = 6,
|
|
DYNAMIC_ABILITY_EFFECT_DEPLETE = 7,
|
|
DYNAMIC_ABILITY_EFFECT_COUNTERSONEONE = 8,
|
|
DYNAMIC_ABILITY_EFFECT_NB = 9,
|
|
|
|
DYNAMIC_ABILITY_WHO_EACHOTHER = 1,
|
|
DYNAMIC_ABILITY_WHO_ITSELF = 2,
|
|
DYNAMIC_ABILITY_WHO_TARGETCONTROLLER = 3,
|
|
DYNAMIC_ABILITY_WHO_TARGETOPPONENT = 4,
|
|
DYNAMIC_ABILITY_WHO_TOSOURCE = 5,
|
|
DYNAMIC_ABILITY_WHO_SOURCECONTROLLER = 6,
|
|
DYNAMIC_ABILITY_WHO_SOURCEOPPONENT = 7,
|
|
DYNAMIC_ABILITY_WHO_NB = 8,
|
|
|
|
};
|
|
int type;
|
|
int effect;
|
|
int who;
|
|
int sourceamount;
|
|
int targetamount;
|
|
int amountsource;
|
|
bool eachother;
|
|
bool tosrc;
|
|
MTGCardInstance * OriginalSrc;
|
|
MTGCardInstance * storedTarget;
|
|
MTGAbility * storedAbility;
|
|
MTGAbility * clonedStored;
|
|
MTGAbility * mainAbility;
|
|
string menu;
|
|
|
|
AADynamic(GameObserver* observer, int id, MTGCardInstance * card, Damageable * _target,int type = 0,int effect = 0,int who = 0,int amountsource = 1,MTGAbility * storedAbility = NULL, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
int activateMainAbility(MTGAbility * toActivate,MTGCardInstance * source , Damageable * target);
|
|
int activateStored();
|
|
const string getMenuText();
|
|
AADynamic * clone() const;
|
|
~AADynamic();
|
|
};
|
|
|
|
/* switch power and toughness of target */
|
|
class ASwapPT: public InstantAbility
|
|
{
|
|
public:
|
|
ASwapPT(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
InstantAbility(observer, _id, _source, _target)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
while (_target->next)
|
|
_target = _target->next; //This is for cards such as rampant growth
|
|
|
|
if(!_target->isSwitchedPT)
|
|
{
|
|
_target->isSwitchedPT = true;
|
|
_target->switchPT(true);
|
|
}
|
|
else
|
|
{
|
|
_target->isSwitchedPT = false;
|
|
_target->switchPT(false);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
while (_target->next)
|
|
_target = _target->next; //This is for cards such as rampant growth
|
|
|
|
if(_target->isSwitchedPT)
|
|
{
|
|
_target->isSwitchedPT = false;
|
|
_target->switchPT(false);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return "Swap power and toughness";
|
|
}
|
|
ASwapPT * clone() const
|
|
{
|
|
return NEW ASwapPT(*this);
|
|
}
|
|
};
|
|
|
|
class AAExchangeLife: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
AAExchangeLife(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, ManaCost * _cost = NULL,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAExchangeLife * clone() const;
|
|
|
|
};
|
|
|
|
// Add life of gives damage if a given zone has more or less than [condition] cards at the beginning of [phase]
|
|
//Ex : the rack, ivory tower...
|
|
class ALifeZoneLink: public MTGAbility
|
|
{
|
|
public:
|
|
int phase;
|
|
int condition;
|
|
int life;
|
|
int controller;
|
|
int nbcards;
|
|
MTGGameZone * zone;
|
|
ALifeZoneLink(GameObserver* observer, int _id, MTGCardInstance * card, int _phase, int _condition, int _life = -1, int _controller = 0,
|
|
MTGGameZone * _zone = NULL) :
|
|
MTGAbility(observer, _id, card)
|
|
{
|
|
phase = _phase;
|
|
condition = _condition;
|
|
controller = _controller;
|
|
life = _life;
|
|
zone = _zone;
|
|
if (zone == NULL)
|
|
{
|
|
if (controller)
|
|
{
|
|
zone = game->currentPlayer->game->hand;
|
|
}
|
|
else
|
|
{
|
|
zone = game->opponent()->game->hand;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == phase && !game->currentPlayer->inPlay()->hasAbility(Constants::CANTCHANGELIFE))
|
|
{
|
|
if ((controller && game->currentPlayer == source->controller()) || (!controller && game->currentPlayer
|
|
!= source->controller()))
|
|
{
|
|
if ((condition < 0 && zone->nb_cards < -condition) || (condition > 0 && zone->nb_cards > condition))
|
|
{
|
|
int diff = zone->nb_cards - condition;
|
|
if (condition < 0) diff = -condition - zone->nb_cards;
|
|
if (life > 0)
|
|
{
|
|
game->currentPlayer->life += life * diff;
|
|
}
|
|
else
|
|
{
|
|
game->mLayers->stackLayer()->addDamage(source, game->currentPlayer, -life * diff);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ALifeZoneLink ::: phase : " << phase << " ; condition : " << condition << " ; life : " << life
|
|
<< " ; controller : " << controller << " ; nbcards : " << nbcards << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
ALifeZoneLink * clone() const
|
|
{
|
|
return NEW ALifeZoneLink(*this);
|
|
}
|
|
};
|
|
|
|
//Creatures that cannot attack if opponent has not a given type of land, and die if controller has not this type of land
|
|
//Ex : pirate ship...
|
|
class AStrongLandLinkCreature: public MTGAbility
|
|
{
|
|
public:
|
|
char land[20];
|
|
AStrongLandLinkCreature(GameObserver* observer, int _id, MTGCardInstance * _source, const char * _land) :
|
|
MTGAbility(observer, _id, _source)
|
|
{
|
|
sprintf(land, "%s", _land);
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
if (newPhase != currentPhase && (newPhase == MTG_PHASE_COMBATBEGIN || newPhase == MTG_PHASE_COMBATATTACKERS))
|
|
{
|
|
if (source->controller()->opponent()->game->inPlay->hasType(land))
|
|
{
|
|
source->basicAbilities[(int)Constants::CANTATTACK] = 0;
|
|
}
|
|
else
|
|
{
|
|
source->basicAbilities[(int)Constants::CANTATTACK] = 1;
|
|
}
|
|
}
|
|
Player * player = source->controller();
|
|
if (!player->game->inPlay->hasType(land))
|
|
{
|
|
player->game->putInGraveyard(source);
|
|
}
|
|
}
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AStrongLandLinkCreature ::: land : " << land << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AStrongLandLinkCreature * clone() const
|
|
{
|
|
return NEW AStrongLandLinkCreature(*this);
|
|
}
|
|
};
|
|
|
|
//Steal control of a target
|
|
class AControlStealAura: public MTGAbility
|
|
{
|
|
public:
|
|
Player * originalController;
|
|
AControlStealAura(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
MTGAbility(observer, _id, _source, _target)
|
|
{
|
|
originalController = _target->controller();
|
|
MTGCardInstance * copy = _target->changeController(game->currentlyActing());
|
|
target = copy;
|
|
source->target = copy;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
Player * p = _target->controller();
|
|
if (p && p->game->inPlay->hasCard(_target))
|
|
{ //if the target is still in game -> spell was destroyed
|
|
_target->changeController(originalController);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AControlStealAura ::: originalController : " << originalController << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AControlStealAura * clone() const
|
|
{
|
|
return NEW AControlStealAura(*this);
|
|
}
|
|
};
|
|
//bloodthirst ability------------------------------------------
|
|
class ABloodThirst: public MTGAbility
|
|
{
|
|
public:
|
|
int amount;
|
|
ABloodThirst(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, int amount) :
|
|
MTGAbility(observer, id, source, target), amount(amount)
|
|
{
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
if (_target->controller()->opponent()->damaged() > 0)
|
|
{
|
|
_target->counters->addCounter(1, 1);
|
|
}
|
|
}
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
ABloodThirst * clone() const
|
|
{
|
|
return NEW ABloodThirst(*this);
|
|
}
|
|
|
|
~ABloodThirst()
|
|
{
|
|
}
|
|
};
|
|
|
|
//reduce or increase manacost of target by color:amount------------------------------------------
|
|
class AAlterCost: public MTGAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * manaReducer;
|
|
int amount;
|
|
int tempAmount;
|
|
int type;
|
|
AAlterCost(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, int amount, int type);
|
|
int addToGame();
|
|
int destroy();
|
|
int testDestroy();
|
|
void refreshCost(MTGCardInstance * card = NULL);
|
|
void increaseTheCost(MTGCardInstance * card = NULL);
|
|
void decreaseTheCost(MTGCardInstance * card = NULL);
|
|
AAlterCost * clone() const;
|
|
~AAlterCost();
|
|
};
|
|
|
|
//------------------------------------
|
|
class ATransformer: public MTGAbility
|
|
{
|
|
public:
|
|
list<int> abilities;
|
|
list<int> types;
|
|
list<int> colors;
|
|
list<int> oldcolors;
|
|
list<int> oldtypes;
|
|
vector<int> dontremove;
|
|
bool addNewColors;
|
|
bool remove;
|
|
bool removeCreatureSubtypes;
|
|
bool removeTypes;
|
|
string menu;
|
|
|
|
string newpower;
|
|
bool newpowerfound;
|
|
string newtoughness;
|
|
bool newtoughnessfound;
|
|
|
|
map<Damageable *, vector<MTGAbility *> > newAbilities;
|
|
vector<string> newAbilitiesList;
|
|
bool newAbilityFound;
|
|
bool aForever;
|
|
bool UYNT;
|
|
int myCurrentTurn;
|
|
string menutext; //this overrides the previous.
|
|
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 = false,bool aForever = false ,bool UYNT = false,string menutext = "");
|
|
int addToGame();
|
|
int reapplyCountersBonus(MTGCardInstance * rtarget= NULL,bool powerapplied=false,bool toughnessapplied=false);
|
|
int testDestroy();
|
|
int destroy();
|
|
const string getMenuText();
|
|
ATransformer * clone() const;
|
|
~ATransformer();
|
|
};
|
|
|
|
//Adds types/abilities/changes color to a card (generally until end of turn)
|
|
class ATransformerInstant: public InstantAbility
|
|
{
|
|
public:
|
|
ATransformer * ability;
|
|
string newpower;
|
|
bool newpowerfound;
|
|
string newtoughness;
|
|
bool newtoughnessfound;
|
|
vector<string> newAbilitiesList;
|
|
map<Damageable *, vector<MTGAbility *> > newAbilities;
|
|
bool newAbilityFound;
|
|
bool aForever;
|
|
bool UYNT;
|
|
string menu;
|
|
|
|
ATransformerInstant(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, string types = "", string abilities = "",string newpower = "",bool newpowerfound = false,string newtoughness = "",bool newtoughnessfound = false,vector<string>newAbilitiesList = vector<string>(),bool newAbilityFound = false,bool aForever = false, bool UYNT = false,string menutext = "");
|
|
int resolve();
|
|
const string getMenuText();
|
|
ATransformerInstant * clone() const;
|
|
~ATransformerInstant();
|
|
};
|
|
|
|
//Adds types/abilities/changes color to a card (generally until end of turn)
|
|
class PTInstant: public InstantAbility
|
|
{
|
|
public:
|
|
APowerToughnessModifier * ability;
|
|
WParsedPT * wppt;
|
|
string s;
|
|
bool nonstatic;
|
|
WParsedPT * newWppt;
|
|
PTInstant(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, WParsedPT * wppt,string s = "",bool nonstatic = false);
|
|
int resolve();
|
|
const string getMenuText();
|
|
PTInstant * clone() const;
|
|
~PTInstant();
|
|
};
|
|
|
|
//ExaltedAbility (Shards of Alara)
|
|
class AExalted: public TriggeredAbility
|
|
{
|
|
public:
|
|
int power, toughness;
|
|
MTGCardInstance * luckyWinner;
|
|
AExalted(GameObserver* observer, int _id, MTGCardInstance * _source, int _power = 1, int _toughness = 1) :
|
|
TriggeredAbility(observer, _id, _source), power(_power), toughness(_toughness)
|
|
{
|
|
luckyWinner = NULL;
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if (WEventPhaseChange* pe = dynamic_cast<WEventPhaseChange*>(event))
|
|
{
|
|
if (luckyWinner && MTG_PHASE_AFTER_EOT == pe->from->id)
|
|
{
|
|
luckyWinner = NULL;
|
|
}
|
|
|
|
if (MTG_PHASE_COMBATATTACKERS == pe->from->id)
|
|
{
|
|
int nbattackers = 0;
|
|
MTGGameZone * z = source->controller()->game->inPlay;
|
|
int nbcards = z->nb_cards;
|
|
for (int i = 0; i < nbcards; ++i)
|
|
{
|
|
MTGCardInstance * c = z->cards[i];
|
|
if (c->attacker)
|
|
{
|
|
nbattackers++;
|
|
luckyWinner = c;
|
|
}
|
|
}
|
|
if (nbattackers == 1)
|
|
return 1;
|
|
else
|
|
luckyWinner = NULL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
if (!luckyWinner) return 0;
|
|
PTInstant * a = NEW PTInstant(game, this->GetId(), source, luckyWinner,NEW WParsedPT(1,1));
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source,luckyWinner, a);
|
|
wrapper->addToGame();
|
|
luckyWinner = NULL;
|
|
return 1;
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return "Exalted";
|
|
}
|
|
|
|
AExalted * clone() const
|
|
{
|
|
return NEW AExalted(*this);
|
|
}
|
|
};
|
|
|
|
//switch p/t ueot
|
|
class ASwapPTUEOT: public InstantAbility
|
|
{
|
|
public:
|
|
ASwapPT * ability;
|
|
ASwapPTUEOT(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target);
|
|
int resolve();
|
|
const string getMenuText();
|
|
ASwapPTUEOT * clone() const;
|
|
~ASwapPTUEOT();
|
|
};
|
|
|
|
class APreventDamageTypes: public MTGAbility
|
|
{
|
|
public:
|
|
string to, from;
|
|
REDamagePrevention * re;
|
|
int type;
|
|
|
|
APreventDamageTypes(GameObserver* observer, int id, MTGCardInstance * source, string to, string from, int type = 0);
|
|
int addToGame();
|
|
int destroy();
|
|
APreventDamageTypes * clone() const;
|
|
~APreventDamageTypes();
|
|
};
|
|
//prevent counters
|
|
class ACounterShroud: public MTGAbility
|
|
{
|
|
public:
|
|
TargetChooser * csTc;
|
|
Counter * counter;
|
|
RECountersPrevention * re;
|
|
ACounterShroud(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target,TargetChooser * tc, Counter * counter = NULL);
|
|
int addToGame();
|
|
int destroy();
|
|
ACounterShroud * clone() const;
|
|
~ACounterShroud();
|
|
};
|
|
//track an effect using counters.
|
|
class ACounterTracker: public MTGAbility
|
|
{
|
|
public:
|
|
string scounter;
|
|
int removed;
|
|
ACounterTracker(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, string scounter = "");
|
|
int addToGame();
|
|
int destroy();
|
|
int testDestroy();
|
|
ACounterTracker * clone() const;
|
|
~ACounterTracker();
|
|
};
|
|
//Remove all abilities from target
|
|
class ALoseAbilities: public MTGAbility
|
|
{
|
|
public:
|
|
vector <MTGAbility *> storedAbilities;
|
|
ALoseAbilities(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target);
|
|
int addToGame();
|
|
int destroy();
|
|
ALoseAbilities * clone() const;
|
|
};
|
|
|
|
//Remove subtypes (of a given type) from target
|
|
class ALoseSubtypes: public MTGAbility
|
|
{
|
|
public:
|
|
int parentType;
|
|
vector <int> storedSubtypes;
|
|
ALoseSubtypes(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, int parentType);
|
|
int addToGame();
|
|
int destroy();
|
|
ALoseSubtypes * clone() const;
|
|
};
|
|
|
|
//Adds types/abilities/P/T to a card (until end of turn)
|
|
class APreventDamageTypesUEOT: public InstantAbility
|
|
{
|
|
public:
|
|
APreventDamageTypes * ability;
|
|
vector<APreventDamageTypes *> clones;
|
|
int type;
|
|
APreventDamageTypesUEOT(GameObserver* observer, int id, MTGCardInstance * source, string to, string from, int type = 0);
|
|
int resolve();
|
|
int destroy();
|
|
const string getMenuText();
|
|
APreventDamageTypesUEOT * clone() const;
|
|
~APreventDamageTypesUEOT();
|
|
};
|
|
|
|
//vanishing
|
|
class AVanishing: public MTGAbility
|
|
{
|
|
public:
|
|
int timeLeft;
|
|
int amount;
|
|
string counterName;
|
|
int next;
|
|
|
|
AVanishing(GameObserver* observer, int _id, MTGCardInstance * card, ManaCost * _cost, int restrictions = 0,int amount = 0,string counterName = "");
|
|
void Update(float dt);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AVanishing * clone() const;
|
|
~AVanishing();
|
|
};
|
|
|
|
//Produce Mana when tapped for mana...
|
|
class AProduceMana: public MTGAbility
|
|
{
|
|
public:
|
|
string ManaDescription;
|
|
string mana[5];
|
|
|
|
AProduceMana(GameObserver* observer, int _id, MTGCardInstance * _source, string _ManaDescription);
|
|
int receiveEvent(WEvent * event);
|
|
int produce();
|
|
const string getMenuText();
|
|
//virtual ostream& toString(ostream& out) const;
|
|
AProduceMana * clone() const;
|
|
~AProduceMana();
|
|
};
|
|
|
|
//Produce Mana when a mana is engaged...
|
|
class AEngagedManaAbility: public MTGAbility
|
|
{
|
|
public:
|
|
string colorname;
|
|
AEngagedManaAbility(GameObserver* observer, int _id, MTGCardInstance * _source, string _colorname) :
|
|
MTGAbility(observer, _id, _source)
|
|
{
|
|
colorname = _colorname;
|
|
}
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
if(WEventEngageMana * isManaProduced = dynamic_cast<WEventEngageMana *> (event))
|
|
{
|
|
if ((isManaProduced->card == source) && isManaProduced->color == Constants::GetColorStringIndex(colorname))
|
|
{
|
|
source->controller()->getManaPool()->add(Constants::GetColorStringIndex(colorname),1,source,true);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
AEngagedManaAbility * clone() const
|
|
{
|
|
return NEW AEngagedManaAbility(*this);
|
|
}
|
|
};
|
|
|
|
//Upkeep Cost
|
|
class AUpkeep: public ActivatedAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int paidThisTurn;
|
|
int phase;
|
|
int once;
|
|
bool Cumulative;
|
|
int currentage;
|
|
ManaCost * backupMana;
|
|
|
|
AUpkeep(GameObserver* observer, int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int restrictions = 0, int _phase =
|
|
MTG_PHASE_UPKEEP, int _once = 0,bool Cumulative = false);
|
|
int receiveEvent(WEvent * event);
|
|
void Update(float dt);
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
virtual ostream& toString(ostream& out) const;
|
|
AUpkeep * clone() const;
|
|
~AUpkeep();
|
|
};
|
|
|
|
//phase based actions
|
|
class APhaseAction: public MTGAbility
|
|
{
|
|
public:
|
|
string psMenuText;
|
|
int abilityId;
|
|
string sAbility;
|
|
int phase;
|
|
MTGAbility * ability;
|
|
bool forcedestroy;
|
|
bool next;
|
|
bool myturn;
|
|
bool opponentturn;
|
|
bool once;
|
|
bool checkexile;
|
|
Player * abilityOwner;
|
|
|
|
APhaseAction(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * target, string sAbility, int restrictions = 0, int _phase =
|
|
MTG_PHASE_UPKEEP,bool forcedestroy = false,bool next = true,bool myturn = true,bool opponentturn = true,bool once = false, bool checkexile = false);
|
|
void Update(float dt);
|
|
int resolve();
|
|
const string getMenuText();
|
|
APhaseAction * clone() const;
|
|
~APhaseAction();
|
|
};
|
|
|
|
//Adds types/abilities/P/T to a card (until end of turn)
|
|
class APhaseActionGeneric: public InstantAbility
|
|
{
|
|
public:
|
|
string sAbility;
|
|
APhaseAction * ability;
|
|
APhaseActionGeneric(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * target, string sAbility, int restrictions = 0, int _phase =
|
|
MTG_PHASE_UPKEEP,bool forcedestroy = false,bool next = true,bool myturn = false,bool opponentturn = false,bool once = false,bool checkexile = false);
|
|
int resolve();
|
|
const string getMenuText();
|
|
APhaseActionGeneric * clone() const;
|
|
~APhaseActionGeneric();
|
|
|
|
};
|
|
|
|
//AAttackSetCost
|
|
class AAttackSetCost: public MTGAbility
|
|
{
|
|
public:
|
|
string number;
|
|
bool pw;
|
|
AAttackSetCost(GameObserver* observer, int _id, MTGCardInstance * _source, string number, bool pw = false);
|
|
void Update(float dt);
|
|
int addToGame();
|
|
int destroy();
|
|
const string getMenuText();
|
|
AAttackSetCost * clone() const;
|
|
};
|
|
|
|
//ABlockSetCost
|
|
class ABlockSetCost: public MTGAbility
|
|
{
|
|
public:
|
|
string number;
|
|
ABlockSetCost(GameObserver* observer, int _id, MTGCardInstance * _source, string number);
|
|
void Update(float dt);
|
|
int addToGame();
|
|
int destroy();
|
|
const string getMenuText();
|
|
ABlockSetCost * clone() const;
|
|
};
|
|
|
|
//AShackle
|
|
class AShackle: public MTGAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * Shackled;
|
|
Player * previousController;
|
|
bool resolved;
|
|
AShackle(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target);
|
|
void Update(float dt);
|
|
void resolveShackle();
|
|
int resolve();
|
|
const string getMenuText();
|
|
AShackle * clone() const;
|
|
~AShackle();
|
|
private:
|
|
void returntoOwner(MTGCardInstance *_target);
|
|
};
|
|
|
|
//ShackleWrapper
|
|
class AShackleWrapper: public InstantAbility
|
|
{
|
|
public:
|
|
AShackle * ability;
|
|
AShackleWrapper(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AShackleWrapper * clone() const;
|
|
~AShackleWrapper();
|
|
|
|
};
|
|
//Grant
|
|
class AGrant : public MTGAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * Blessed;
|
|
bool resolved;
|
|
MTGAbility * Granted;
|
|
MTGAbility * toGrant;
|
|
AGrant(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, MTGAbility * toGrant);
|
|
void Update(float dt);
|
|
void resolveGrant();
|
|
int resolve();
|
|
const string getMenuText();
|
|
AGrant * clone() const;
|
|
~AGrant();
|
|
private:
|
|
void removeGranted(MTGCardInstance *_target);
|
|
};
|
|
|
|
//GrantWrapper
|
|
class AGrantWrapper : public InstantAbility
|
|
{
|
|
public:
|
|
AGrant * ability;
|
|
MTGAbility * Granted;
|
|
AGrantWrapper(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, MTGAbility * toGrant);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AGrantWrapper * clone() const;
|
|
~AGrantWrapper();
|
|
|
|
};
|
|
//ABlink
|
|
class ABlink: public MTGAbility
|
|
{
|
|
public:
|
|
bool blinkueot;
|
|
bool blinkForSource;
|
|
bool blinkhand;
|
|
MTGCardInstance * Blinked;
|
|
bool resolved;
|
|
MTGAbility * stored;
|
|
ABlink(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target, bool blinkueot = false, bool blinkForSource = false, bool blinkhand = false, MTGAbility * stored = NULL);
|
|
void Update(float dt);
|
|
void resolveBlink();
|
|
int resolve();
|
|
const string getMenuText();
|
|
ABlink * clone() const;
|
|
~ABlink();
|
|
private:
|
|
void returnCardIntoPlay(MTGCardInstance *_target);
|
|
};
|
|
|
|
//blinkinstant
|
|
class ABlinkGeneric: public InstantAbility
|
|
{
|
|
public:
|
|
bool blinkueot;
|
|
bool blinkForSource;
|
|
bool blinkhand;
|
|
ABlink * ability;
|
|
MTGAbility * stored;
|
|
ABlinkGeneric(GameObserver* observer, int _id, MTGCardInstance * card, MTGCardInstance * _target,bool blinkueot=false,bool blinkForSource = false,bool blinkhand = false,MTGAbility * stored = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
ABlinkGeneric * clone() const;
|
|
~ABlinkGeneric();
|
|
|
|
};
|
|
|
|
class AManaPoolSaver: public MTGAbility
|
|
{
|
|
public:
|
|
string Color;
|
|
bool OtherPlayer;
|
|
AManaPoolSaver(GameObserver* observer, int id, MTGCardInstance * source,string Color = "",bool otherPlayer = false);
|
|
int addToGame();
|
|
int destroy();
|
|
AManaPoolSaver * clone() const;
|
|
~AManaPoolSaver();
|
|
};
|
|
|
|
class ADrawReplacer: public MTGAbility
|
|
{
|
|
public:
|
|
REDrawReplacement * re;
|
|
MTGAbility * replacer;
|
|
bool OtherPlayer;
|
|
ADrawReplacer(GameObserver* observer, int id, MTGCardInstance * source, MTGAbility * _replace = NULL,bool otherPlayer = false);
|
|
int addToGame();
|
|
int destroy();
|
|
ADrawReplacer * clone() const;
|
|
~ADrawReplacer();
|
|
};
|
|
/*
|
|
Specific Classes
|
|
*/
|
|
|
|
// 1092 Specific to Aladdin's Lamp
|
|
class AAladdinsLamp: public TargetAbility
|
|
{
|
|
public:
|
|
CardDisplay cd;
|
|
int nbcards;
|
|
int init;
|
|
|
|
AAladdinsLamp(GameObserver* observer, int id, MTGCardInstance * card) :
|
|
TargetAbility(observer, id, card), cd(1, game, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, NULL)
|
|
{
|
|
setCost(NEW ManaCost(), true);
|
|
getCost()->x();
|
|
int zones[] = { MTGGameZone::MY_LIBRARY };
|
|
tc = NEW TargetZoneChooser(game, zones, 1, source);
|
|
nbcards = 0;
|
|
init = 0;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (waitingForAnswer)
|
|
{
|
|
if (!init)
|
|
{
|
|
cd.resetObjects();
|
|
int wished = game->currentlyActing()->getManaPool()->getConvertedCost();
|
|
game->currentlyActing()->getManaPool()->pay(getCost());
|
|
nbcards = 0;
|
|
MTGGameZone * library = game->currentlyActing()->game->library;
|
|
while (nbcards < wished)
|
|
{
|
|
cd.AddCard(library->cards[library->nb_cards - 1 - nbcards]);
|
|
nbcards++;
|
|
}
|
|
init = 1;
|
|
Render(dt);
|
|
}
|
|
cd.Update(dt);
|
|
|
|
// cd.CheckUserInput(dt);
|
|
}
|
|
}
|
|
|
|
using TargetAbility::Render;
|
|
void Render(float)
|
|
{
|
|
if (waitingForAnswer)
|
|
{
|
|
cd.Render();
|
|
}
|
|
}
|
|
|
|
int fireAbility()
|
|
{
|
|
source->tap();
|
|
MTGLibrary * library = game->currentlyActing()->game->library;
|
|
MTGHand * hand = game->currentlyActing()->game->hand;
|
|
MTGCardInstance * card = library->removeCard(tc->getNextCardTarget());
|
|
//library->shuffleTopToBottom(nbcards - 1);
|
|
hand->addCard(card);
|
|
init = 0;
|
|
return 1;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 1;
|
|
}
|
|
;
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AAladdinsLamp ::: cd : " << cd << " ; nbcards : " << nbcards << " ; init : " << init << " (";
|
|
return TargetAbility::toString(out) << ")";
|
|
}
|
|
AAladdinsLamp * clone() const
|
|
{
|
|
return NEW AAladdinsLamp(*this);
|
|
}
|
|
};
|
|
|
|
// Armageddon Clock
|
|
class AArmageddonClock: public MTGAbility
|
|
{
|
|
public:
|
|
int counters;
|
|
ManaCost cost;
|
|
AArmageddonClock(GameObserver* observer, int id, MTGCardInstance * _source) :
|
|
MTGAbility(observer, id, _source)
|
|
{
|
|
counters = 0;
|
|
std::vector<int16_t> _cost;
|
|
_cost.push_back(Constants::MTG_COLOR_ARTIFACT);
|
|
_cost.push_back(4);
|
|
cost = ManaCost(_cost, 1);
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
if (newPhase != currentPhase)
|
|
{
|
|
if (newPhase == MTG_PHASE_UPKEEP && game->currentPlayer->game->inPlay->hasCard(source))
|
|
{
|
|
counters++;
|
|
}
|
|
else if (newPhase == MTG_PHASE_DRAW && counters > 0 && game->currentPlayer->game->inPlay->hasCard(source))
|
|
{ //End of upkeep = beginning of draw
|
|
game->mLayers->stackLayer()->addDamage(source, game->players[0],
|
|
counters);
|
|
game->mLayers->stackLayer()->addDamage(source, game->players[1],
|
|
counters);
|
|
}
|
|
}
|
|
}
|
|
int isReactingToClick(MTGCardInstance * _card, ManaCost * = NULL)
|
|
{
|
|
if (counters > 0 && _card == source && currentPhase == MTG_PHASE_UPKEEP)
|
|
{
|
|
if (game->currentlyActing()->getManaPool()->canAfford(&cost))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card)
|
|
{
|
|
if (!isReactingToClick(_card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(&cost);
|
|
counters--;
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AArmageddonClock ::: counters : " << counters << " ; cost : " << cost << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AArmageddonClock * clone() const
|
|
{
|
|
return NEW AArmageddonClock(*this);
|
|
}
|
|
};
|
|
|
|
//1102: Conservator
|
|
class AConservator: public MTGAbility
|
|
{
|
|
public:
|
|
int canprevent;
|
|
ManaCost cost;
|
|
AConservator(GameObserver* observer, int _id, MTGCardInstance * _source) :
|
|
MTGAbility(observer, _id, _source)
|
|
{
|
|
canprevent = 0;
|
|
std::vector<int16_t> _cost;
|
|
_cost.push_back(Constants::MTG_COLOR_ARTIFACT);
|
|
_cost.push_back(2);
|
|
cost = ManaCost(_cost, 1);
|
|
}
|
|
|
|
int alterDamage(Damage * damage)
|
|
{
|
|
if (canprevent && damage->target == source->controller())
|
|
{
|
|
if (damage->damage >= canprevent)
|
|
{
|
|
damage->damage -= canprevent;
|
|
canprevent = 0;
|
|
}
|
|
else
|
|
{
|
|
canprevent -= damage->damage;
|
|
damage->damage = 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
int alterDamage()
|
|
{
|
|
if (canprevent)
|
|
{
|
|
ActionStack * stack = game->mLayers->stackLayer();
|
|
for (int i = stack->mObjects.size() - 1; i >= 0; i--)
|
|
{
|
|
if (!canprevent) return 1;
|
|
Interruptible * current = ((Interruptible *) stack->mObjects[i]);
|
|
if (current->type == ACTION_DAMAGE && current->state == NOT_RESOLVED)
|
|
{
|
|
Damage * damage = (Damage *) current;
|
|
alterDamage(damage);
|
|
}
|
|
else if (current->type == ACTION_DAMAGES && current->state == NOT_RESOLVED)
|
|
{
|
|
DamageStack * damages = (DamageStack *) current;
|
|
for (int j = damages->mObjects.size() - 1; j >= 0; j--)
|
|
{
|
|
alterDamage(((Damage *) damages->mObjects[j]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
alterDamage();
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card, ManaCost * = NULL)
|
|
{
|
|
if (_card == source && game->currentlyActing()->game->inPlay->hasCard(source) && !_card->isTapped())
|
|
{
|
|
if (game->currentlyActing()->getManaPool()->canAfford(&cost))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card)
|
|
{
|
|
if (!isReactingToClick(_card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(&cost);
|
|
source->tap();
|
|
canprevent = 2;
|
|
alterDamage();
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AConservator ::: canprevent : " << canprevent << " ; cost : " << cost << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AConservator * clone() const
|
|
{
|
|
return NEW AConservator(*this);
|
|
}
|
|
};
|
|
|
|
//Kjeldoran Frostbeast
|
|
class AKjeldoranFrostbeast: public MTGAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * opponents[20];
|
|
int nbOpponents;
|
|
AKjeldoranFrostbeast(GameObserver* observer, int _id, MTGCardInstance * _source) :
|
|
MTGAbility(observer, _id, _source)
|
|
{
|
|
nbOpponents = 0;
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
if (newPhase != currentPhase)
|
|
{
|
|
if (newPhase == MTG_PHASE_COMBATEND)
|
|
{
|
|
nbOpponents = 0;
|
|
MTGCardInstance * opponent = source->getNextOpponent();
|
|
while (opponent)
|
|
{
|
|
opponents[nbOpponents] = opponent;
|
|
nbOpponents++;
|
|
opponent = source->getNextOpponent(opponent);
|
|
}
|
|
if (source->isInPlay(game))
|
|
{
|
|
for (int i = 0; i < nbOpponents; i++)
|
|
{
|
|
opponents[i]->destroy();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int testDestroy()
|
|
{
|
|
if (!game->isInPlay(source) && currentPhase != MTG_PHASE_UNTAP)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return MTGAbility::testDestroy();
|
|
}
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AKjeldoranFrostbeast ::: opponents : " << opponents << " ; nbOpponents : " << nbOpponents << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AKjeldoranFrostbeast * clone() const
|
|
{
|
|
return NEW AKjeldoranFrostbeast(*this);
|
|
}
|
|
};
|
|
|
|
//1143 Animate Dead
|
|
class AAnimateDead: public MTGAbility
|
|
{
|
|
public:
|
|
AAnimateDead(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
MTGAbility(observer, _id, _source, _target)
|
|
{
|
|
MTGCardInstance * card = _target;
|
|
|
|
//Put the card in play again, with all its abilities !
|
|
//AbilityFactory af;
|
|
MTGCardInstance * copy = source->controller()->game->putInZone(card, _target->controller()->game->graveyard,
|
|
source->controller()->game->temp);
|
|
Spell * spell = NEW Spell(game, copy);
|
|
spell->resolve();
|
|
target = spell->source;
|
|
card = spell->source;
|
|
card->power--;
|
|
card->life = card->toughness;
|
|
delete spell;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
MTGCardInstance * card = (MTGCardInstance *) target;
|
|
card->power++;
|
|
card->controller()->game->putInZone(card, card->controller()->game->inPlay, card->owner->game->graveyard);
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AAnimateDead ::: (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AAnimateDead * clone() const
|
|
{
|
|
return NEW AAnimateDead(*this);
|
|
}
|
|
};
|
|
|
|
//1159 Erg Raiders
|
|
class AErgRaiders: public MTGAbility
|
|
{
|
|
public:
|
|
int attackedThisTurn;
|
|
AErgRaiders(GameObserver* observer, int _id, MTGCardInstance * _source) :
|
|
MTGAbility(observer, _id, _source)
|
|
{
|
|
attackedThisTurn = 1;
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
if (newPhase != currentPhase)
|
|
{
|
|
Player * controller = source->controller();
|
|
if(newPhase == MTG_PHASE_ENDOFTURN)
|
|
{
|
|
if(!attackedThisTurn && game->currentPlayer == source->controller() && !source->fresh)
|
|
game->mLayers->stackLayer()->addDamage(source, controller, 2);
|
|
}
|
|
else if (newPhase == MTG_PHASE_UNTAP)
|
|
{
|
|
|
|
if (game->currentPlayer == controller)
|
|
{
|
|
attackedThisTurn = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
WEventCardAttacked * attacked = dynamic_cast<WEventCardAttacked *> (event);
|
|
if (attacked && !attacked->card->didblocked && attacked->card == source)
|
|
{
|
|
attackedThisTurn = 1;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
AErgRaiders * clone() const
|
|
{
|
|
return NEW AErgRaiders(*this);
|
|
}
|
|
};
|
|
|
|
//Fastbond
|
|
class AFastbond: public TriggeredAbility
|
|
{
|
|
public:
|
|
|
|
TargetChooser * counter;
|
|
MaxPerTurnRestriction * landsRestriction;
|
|
int landsPlayedThisTurn;
|
|
AFastbond(GameObserver* observer, int _id, MTGCardInstance * card) :
|
|
TriggeredAbility(observer, _id, card)
|
|
{
|
|
|
|
counter = NEW TypeTargetChooser(game, "land");
|
|
landsPlayedThisTurn = source->controller()->game->inPlay->seenThisTurn(counter, Constants::CAST_ALL);
|
|
PlayRestrictions * restrictions = source->controller()->game->playRestrictions;
|
|
landsRestriction = restrictions->getMaxPerTurnRestrictionByTargetChooser(counter);
|
|
restrictions->removeRestriction(landsRestriction);
|
|
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UNTAP)
|
|
{
|
|
landsPlayedThisTurn = 0;
|
|
}
|
|
TriggeredAbility::Update(dt);
|
|
}
|
|
|
|
int trigger()
|
|
{
|
|
int landsPlayedThisTurnUpdated = source->controller()->game->inPlay->seenThisTurn(counter, Constants::CAST_ALL);
|
|
if (landsPlayedThisTurnUpdated > 1 && landsPlayedThisTurnUpdated > landsPlayedThisTurn)
|
|
{
|
|
landsPlayedThisTurn = landsPlayedThisTurnUpdated;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
game->mLayers->stackLayer()->addDamage(source, source->controller(), 1);
|
|
game->mLayers->stackLayer()->resolve();
|
|
return 1;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
PlayRestrictions * restrictions = source->controller()->game->playRestrictions;
|
|
if(restrictions->getMaxPerTurnRestrictionByTargetChooser(counter))
|
|
return 1;
|
|
|
|
restrictions->addRestriction(landsRestriction);
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
return TriggeredAbility::toString(out) << ")";
|
|
}
|
|
AFastbond * clone() const
|
|
{
|
|
return NEW AFastbond(*this);
|
|
}
|
|
|
|
~AFastbond()
|
|
{
|
|
delete counter;
|
|
}
|
|
};
|
|
|
|
//1117 Jandor's Ring
|
|
class AJandorsRing: public ActivatedAbility
|
|
{
|
|
public:
|
|
AJandorsRing(GameObserver* observer, int _id, MTGCardInstance * _source) :
|
|
ActivatedAbility(observer, _id, _source, NEW ManaCost())
|
|
{
|
|
getCost()->add(Constants::MTG_COLOR_ARTIFACT, 2);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL)
|
|
{
|
|
if (!source->controller()->game->hand->hasCard(source->controller()->game->library->lastCardDrawn)) return 0;
|
|
return ActivatedAbility::isReactingToClick(card, mana);
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
source->controller()->game->putInGraveyard(source->controller()->game->library->lastCardDrawn);
|
|
game->mLayers->stackLayer()->addDraw(source->controller());
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AJandorsRing ::: (";
|
|
return ActivatedAbility::toString(out) << ")";
|
|
}
|
|
AJandorsRing * clone() const
|
|
{
|
|
return NEW AJandorsRing(*this);
|
|
}
|
|
};
|
|
|
|
//Power Leak
|
|
class APowerLeak: public TriggeredAbility
|
|
{
|
|
public:
|
|
int damagesToDealThisTurn;
|
|
ManaCost cost;
|
|
APowerLeak(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
TriggeredAbility(observer, _id, _source, _target)
|
|
{
|
|
cost.add(Constants::MTG_COLOR_ARTIFACT, 1);
|
|
damagesToDealThisTurn = 0;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_UPKEEP && _target->controller() == game->currentPlayer)
|
|
{
|
|
damagesToDealThisTurn = 2;
|
|
}
|
|
TriggeredAbility::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * = NULL)
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (damagesToDealThisTurn && currentPhase == MTG_PHASE_UPKEEP && card == source && _target->controller()
|
|
== game->currentPlayer)
|
|
{
|
|
if (game->currentPlayer->getManaPool()->canAfford(&cost)) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToclick(MTGCardInstance *)
|
|
{
|
|
game->currentPlayer->getManaPool()->pay(&cost);
|
|
damagesToDealThisTurn--;
|
|
return 1;
|
|
}
|
|
|
|
int trigger()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (newPhase != currentPhase && newPhase == MTG_PHASE_DRAW && _target->controller() == game->currentPlayer)
|
|
{
|
|
if (damagesToDealThisTurn) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
game->mLayers->stackLayer()->addDamage(source, _target->controller(), damagesToDealThisTurn);
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "APowerLeak ::: damagesToDealThisTurn : " << damagesToDealThisTurn << " ; cost : " << cost << " (";
|
|
return TriggeredAbility::toString(out) << ")";
|
|
}
|
|
APowerLeak * clone() const
|
|
{
|
|
return NEW APowerLeak(*this);
|
|
}
|
|
};
|
|
|
|
//1176 Sacrifice
|
|
class ASacrifice: public InstantAbility
|
|
{
|
|
public:
|
|
ASacrifice(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
InstantAbility(observer, _id, _source)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target->isInPlay(game))
|
|
{
|
|
game->currentlyActing()->game->putInGraveyard(_target);
|
|
int x = _target->getManaCost()->getConvertedCost();
|
|
game->currentlyActing()->getManaPool()->add(Constants::MTG_COLOR_BLACK, x);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ASacrifice ::: (";
|
|
return InstantAbility::toString(out) << ")";
|
|
}
|
|
ASacrifice * clone() const
|
|
{
|
|
return NEW ASacrifice(*this);
|
|
}
|
|
};
|
|
|
|
//1288 EarthBind
|
|
class AEarthbind: public ABasicAbilityModifier
|
|
{
|
|
public:
|
|
AEarthbind(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
ABasicAbilityModifier(observer, _id, _source, _target, Constants::FLYING, 0)
|
|
{
|
|
if (value_before_modification)
|
|
{
|
|
Damageable * _target = (Damageable *) target;
|
|
game->mLayers->stackLayer()->addDamage(source, _target, 2);
|
|
}
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AEarthbind ::: (";
|
|
return ABasicAbilityModifier::toString(out) << ")";
|
|
}
|
|
AEarthbind * clone() const
|
|
{
|
|
return NEW AEarthbind(*this);
|
|
}
|
|
};
|
|
|
|
//1291 Fireball
|
|
class AFireball: public InstantAbility
|
|
{
|
|
public:
|
|
AFireball(GameObserver* observer, int _id, MTGCardInstance * card, Spell * spell, int x) :
|
|
InstantAbility(observer, _id, card)
|
|
{
|
|
int nbtargets = spell->getNbTargets();
|
|
int totaldamage = x + 1 - nbtargets;
|
|
int individualdamage = 0;
|
|
if (nbtargets) individualdamage = totaldamage / nbtargets;
|
|
Damageable * _target = spell->getNextDamageableTarget();
|
|
while (_target)
|
|
{
|
|
game->mLayers->stackLayer()->addDamage(source, _target, individualdamage);
|
|
_target = spell->getNextDamageableTarget(_target);
|
|
}
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AFireball ::: (";
|
|
return InstantAbility::toString(out) << ")";
|
|
}
|
|
AFireball * clone() const
|
|
{
|
|
return NEW AFireball(*this);
|
|
}
|
|
};
|
|
|
|
//1351 Island Sanctuary
|
|
class AIslandSanctuary: public MTGAbility
|
|
{
|
|
public:
|
|
int initThisTurn;
|
|
vector<MTGCardInstance*> effectedCards;
|
|
AIslandSanctuary(GameObserver* observer, int _id, MTGCardInstance * _source) :
|
|
MTGAbility(observer, _id, _source)
|
|
{
|
|
initThisTurn = 0;
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
if (currentPhase == MTG_PHASE_UNTAP && game->currentPlayer == source->controller())
|
|
{
|
|
initThisTurn = 0;
|
|
for(unsigned int i = 0; i < effectedCards.size(); i++)
|
|
effectedCards.at(i)->basicAbilities[(int)Constants::CANTATTACK] = 0;
|
|
effectedCards.clear();
|
|
}
|
|
if (initThisTurn && currentPhase == MTG_PHASE_COMBATBEGIN && game->currentPlayer != source->controller())
|
|
{
|
|
MTGGameZone * zone = game->currentPlayer->game->inPlay;
|
|
for (int i = 0; i < zone->nb_cards; i++)
|
|
{
|
|
MTGCardInstance * card = zone->cards[i];
|
|
if (!card->has(Constants::FLYING) && !card->has(Constants::ISLANDWALK) && !card->has(Constants::CANTATTACK))
|
|
{
|
|
card->basicAbilities[(int)Constants::CANTATTACK] = 1;
|
|
effectedCards.push_back(card);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * = NULL)
|
|
{
|
|
if (card == source && game->currentPlayer == card->controller())
|
|
{
|
|
|
|
Interruptible * action = game->mLayers->stackLayer()->getAt(-1);
|
|
AADrawer * draw = dynamic_cast <AADrawer *> (action);
|
|
if (draw && draw->aType == MTGAbility::STANDARD_DRAW)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * card)
|
|
{
|
|
if (!isReactingToClick(card)) return 0;
|
|
game->mLayers->stackLayer()->Remove(game->mLayers->stackLayer()->getAt(-1));
|
|
initThisTurn = 1;
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AIslandSanctuary ::: initThisTurn : " << initThisTurn << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AIslandSanctuary * clone() const
|
|
{
|
|
return NEW AIslandSanctuary(*this);
|
|
}
|
|
};
|
|
|
|
//remove or add a phase.
|
|
class APhaseAlter: public TriggeredAbility
|
|
{
|
|
public:
|
|
Targetable * targetPlayerWho;
|
|
bool adding;
|
|
bool applied;
|
|
Player * who;
|
|
string phaseToAlter;
|
|
int phasetoAlter;
|
|
string targetingString;
|
|
string after;
|
|
bool aNext;
|
|
APhaseAlter(GameObserver* observer, int _id, MTGCardInstance * card,Targetable * targetPlayer, bool _adding,string _phaseToAlter,string targeting, bool _aNext,string _after = "") :
|
|
TriggeredAbility(observer, _id, card),targetPlayerWho(targetPlayer),adding(_adding),phaseToAlter(_phaseToAlter),targetingString(targeting),after(_after),aNext(_aNext)
|
|
{
|
|
applied = false;
|
|
who = NULL;
|
|
phasetoAlter = PhaseRing::phaseStrToInt(phaseToAlter);
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(!who)
|
|
{
|
|
Player * targetedPlayer = dynamic_cast<Player*>(target);
|
|
if (targetingString.find("targetedplayer") != string::npos && targetedPlayer)
|
|
{
|
|
who = targetedPlayer;
|
|
}
|
|
if (targetingString.find("controller") != string::npos)
|
|
who = source->controller();
|
|
if (targetingString.find("opponent") != string::npos)
|
|
who = source->controller()->opponent();
|
|
if (targetingString.find("targetcontroller") != string::npos)
|
|
who = source->target?source->target->controller():source->controller();
|
|
if (targetingString.find("owner") != string::npos)
|
|
who = source->owner;
|
|
}
|
|
|
|
if (after == "this")//apply it right now.
|
|
{
|
|
if(!applied)
|
|
if (who == game->currentPlayer)
|
|
{
|
|
after = game->phaseRing->phaseIntToStr(game->oldGamePhase);
|
|
addTheEffect(game->currentPlayer->getId());
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (WEventPhasePreChange* pe = dynamic_cast<WEventPhasePreChange*>(event))
|
|
{
|
|
if (MTG_PHASE_CLEANUP == pe->to->id)
|
|
{
|
|
if( aNext && applied && who != game->currentPlayer)
|
|
{
|
|
forceDestroy = 1;
|
|
}
|
|
}
|
|
if(adding)
|
|
{
|
|
if(!applied)
|
|
if (PhaseRing::phaseStrToInt(after) == pe->to->id && who == game->currentPlayer)
|
|
{
|
|
pe->eventChanged = true;
|
|
return 1;
|
|
}
|
|
}
|
|
else if (PhaseRing::phaseStrToInt(phaseToAlter) == pe->to->id && who == game->currentPlayer)
|
|
{
|
|
pe->eventChanged = true;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
if(who == game->players[i] && game->currentPlayer == game->players[i])
|
|
{
|
|
addTheEffect(i);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
void addTheEffect(int i)
|
|
{
|
|
int turnSteps = game->phaseRing->turn.size();
|
|
if(adding && !applied)
|
|
{
|
|
if(phaseToAlter == "combatphases")
|
|
{
|
|
game->phaseRing->addCombatAfter(game->players[i], PhaseRing::phaseStrToInt(after));
|
|
}
|
|
else if(phaseToAlter == "combatphaseswithmain")
|
|
{
|
|
game->phaseRing->addCombatAfter(game->players[i], PhaseRing::phaseStrToInt(after),true);
|
|
}
|
|
else
|
|
game->phaseRing->addPhaseAfter(PhaseRing::phaseStrToInt(phaseToAlter), game->players[i], PhaseRing::phaseStrToInt(after));
|
|
}
|
|
else if(!adding)
|
|
game->phaseRing->removePhase(PhaseRing::phaseStrToInt(phaseToAlter));
|
|
int turnAfterChange = game->phaseRing->turn.size();
|
|
if(turnSteps != turnAfterChange)
|
|
applied = true;
|
|
return;
|
|
}
|
|
|
|
void removeTheEffect(int i)//readd this!!!!!!!!!!!!!
|
|
{
|
|
if(applied)
|
|
{
|
|
if(adding)
|
|
game->phaseRing->removePhase(PhaseRing::phaseStrToInt(phaseToAlter));
|
|
else
|
|
game->phaseRing->addPhaseAfter(PhaseRing::phaseStrToInt(phaseToAlter), game->players[i], PhaseRing::phaseStrToInt(after));
|
|
applied = false;
|
|
}
|
|
return;
|
|
}
|
|
|
|
int testDestroy()
|
|
{
|
|
if(forceDestroy != -1)
|
|
return 1;
|
|
if(!(source->hasType(Subtypes::TYPE_INSTANT)||source->hasType(Subtypes::TYPE_SORCERY)) && !source->isInPlay(game))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
if(who == game->players[i] && game->currentPlayer == game->players[i])
|
|
{
|
|
removeTheEffect(i);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return "phase alter";
|
|
}
|
|
|
|
APhaseAlter * clone() const
|
|
{
|
|
return NEW APhaseAlter(*this);
|
|
}
|
|
};
|
|
//Generic Millstone
|
|
class AADepleter: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
string nbcardsStr;
|
|
bool toexile;
|
|
bool colorrepeat;
|
|
bool namerepeat;
|
|
AADepleter(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost = NULL,
|
|
int who = TargetChooser::UNSET, bool toexile = false, bool colorrepeat = false, bool namerepeat = false);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AADepleter * clone() const;
|
|
};
|
|
|
|
|
|
//AACascade
|
|
class AACascade: public ActivatedAbility
|
|
{
|
|
public:
|
|
string nbcardsStr;
|
|
MTGCardInstance * castingThis;
|
|
vector<MTGCardInstance*>selectedCards;
|
|
vector<MTGCardInstance *>oldOrder;
|
|
vector<MTGCardInstance *>newOrder;
|
|
AACascade(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, string nbcardsStr, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
void toCastCard(MTGCardInstance * card);
|
|
const string getMenuText();
|
|
AACascade * clone() const;
|
|
};
|
|
|
|
//Generic skip turn/extra turn
|
|
class AAModTurn: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
string nbTurnStr;
|
|
|
|
AAModTurn(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbTurnStr, ManaCost * _cost = NULL,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAModTurn * clone() const;
|
|
};
|
|
|
|
//Shuffle
|
|
class AAShuffle: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
AAShuffle(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int who =
|
|
TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAShuffle * clone() const;
|
|
};
|
|
|
|
//Mulligan
|
|
class AAMulligan: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
AAMulligan(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int who =
|
|
TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAMulligan * clone() const;
|
|
};
|
|
|
|
//Remove Mana From ManaPool
|
|
class AARemoveMana: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
ManaCost * mManaDesc;
|
|
bool mRemoveAll;
|
|
|
|
AARemoveMana(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target, string ManaDesc, int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AARemoveMana * clone() const;
|
|
~AARemoveMana();
|
|
|
|
};
|
|
|
|
//Random Discard
|
|
class AARandomDiscarder: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
string nbcardsStr;
|
|
|
|
AARandomDiscarder(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost = NULL,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AARandomDiscarder * clone() const;
|
|
};
|
|
|
|
//Rampage ability
|
|
class ARampageAbility: public MTGAbility
|
|
{
|
|
public:
|
|
int nbOpponents;
|
|
int PowerModifier;
|
|
int ToughnessModifier;
|
|
int MaxOpponent;
|
|
|
|
ARampageAbility(GameObserver* observer, int _id, MTGCardInstance * _source, int _PowerModifier, int _ToughnessModifier, int _MaxOpponent) :
|
|
MTGAbility(observer, _id, _source)
|
|
{
|
|
PowerModifier = _PowerModifier;
|
|
ToughnessModifier = _ToughnessModifier;
|
|
MaxOpponent = _MaxOpponent;
|
|
nbOpponents = 0;
|
|
}
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
if (dynamic_cast<WEventBlockersChosen*> (event))
|
|
{
|
|
nbOpponents = source->blockers.size();
|
|
if (nbOpponents <= MaxOpponent) return 0;
|
|
if(source->isSwitchedPT)
|
|
{
|
|
source->switchPT(false);
|
|
source->addptbonus(PowerModifier * (nbOpponents - MaxOpponent),ToughnessModifier * (nbOpponents - MaxOpponent));
|
|
source->switchPT(true);
|
|
}
|
|
else
|
|
source->addptbonus(PowerModifier * (nbOpponents - MaxOpponent),ToughnessModifier * (nbOpponents - MaxOpponent));
|
|
}
|
|
else if (WEventPhaseChange* pe = dynamic_cast<WEventPhaseChange*>(event))
|
|
{
|
|
if (MTG_PHASE_AFTER_EOT == pe->to->id && nbOpponents > MaxOpponent)
|
|
{
|
|
if(source->isSwitchedPT)
|
|
{
|
|
source->switchPT(false);
|
|
source->removeptbonus(PowerModifier * (nbOpponents - MaxOpponent),ToughnessModifier * (nbOpponents - MaxOpponent));
|
|
source->switchPT(true);
|
|
}
|
|
else
|
|
source->removeptbonus(PowerModifier * (nbOpponents - MaxOpponent),ToughnessModifier * (nbOpponents - MaxOpponent));
|
|
|
|
nbOpponents = 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
ARampageAbility * clone() const
|
|
{
|
|
return NEW ARampageAbility(*this);
|
|
}
|
|
};
|
|
|
|
//Evole ability
|
|
class AEvolveAbility: public MTGAbility
|
|
{
|
|
public:
|
|
AEvolveAbility(GameObserver* observer, int _id, MTGCardInstance * _source) :
|
|
MTGAbility(observer, _id, _source)
|
|
{
|
|
}
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
WEventZoneChange * enters = dynamic_cast<WEventZoneChange *> (event);
|
|
if (enters && enters->to == enters->card->controller()->game->inPlay)
|
|
if(enters->from != enters->card->controller()->game->inPlay && enters->from != enters->card->controller()->opponent()->game->inPlay) //cards changing from inplay to inplay don't re-enter battlefield
|
|
if(enters->card->controller() == source->controller() && enters->card->isCreature())
|
|
{
|
|
if(enters->card != source && (enters->card->power > source->power || enters->card->toughness > source->toughness))
|
|
{
|
|
source->counters->addCounter(1,1);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
AEvolveAbility * clone() const
|
|
{
|
|
return NEW AEvolveAbility(*this);
|
|
}
|
|
};
|
|
|
|
//Reduce to .. Ali from Cairo...
|
|
class AReduceToAbility: public MTGAbility
|
|
{
|
|
public:
|
|
string life_s;
|
|
|
|
AReduceToAbility(GameObserver* observer, int _id, MTGCardInstance * _source, string _life_s) :
|
|
MTGAbility(observer, _id, _source)
|
|
{
|
|
life_s = _life_s;
|
|
}
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
if(WEventDamage * isDamaged = dynamic_cast<WEventDamage *> (event))
|
|
{
|
|
if (isDamaged->damage->target->type_as_damageable == Damageable::DAMAGEABLE_PLAYER)
|
|
{
|
|
Player * p = (Player *) isDamaged->damage->target;
|
|
WParsedInt lifetoset(life_s, NULL, source);
|
|
if(p && p == source->controller() && p->life <= lifetoset.getValue() && !p->inPlay()->hasAbility(Constants::CANTCHANGELIFE))
|
|
p->life = lifetoset.getValue();
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
AReduceToAbility * clone() const
|
|
{
|
|
return NEW AReduceToAbility(*this);
|
|
}
|
|
};
|
|
|
|
//flanking ability
|
|
class AFlankerAbility: public MTGAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * opponents[20];
|
|
int nbOpponents;
|
|
AFlankerAbility(GameObserver* observer, int _id, MTGCardInstance * _source) :
|
|
MTGAbility(observer, _id, _source)
|
|
{
|
|
nbOpponents = 0;
|
|
}
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
if (dynamic_cast<WEventBlockersChosen*> (event))
|
|
{
|
|
nbOpponents = 0;
|
|
MTGCardInstance * opponent = source->getNextOpponent();
|
|
while (opponent && !opponent->has(Constants::FLANKING) && game->currentlyActing() == source->controller()->opponent())
|
|
{
|
|
opponents[nbOpponents] = opponent;
|
|
nbOpponents++;
|
|
opponent = source->getNextOpponent(opponent);
|
|
}
|
|
for (int i = 0; i < nbOpponents; i++)
|
|
{
|
|
opponents[i]->power -= 1;
|
|
opponents[i]->addToToughness(-1);
|
|
opponents[i]->flanked += 1;
|
|
if (opponents[i]->life == 0)
|
|
{
|
|
opponents[i]->setPower(0);
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AFlankerAbility ::: opponents : " << opponents << " ; nbOpponents : " << nbOpponents << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
|
|
AFlankerAbility * clone() const
|
|
{
|
|
return NEW AFlankerAbility(*this);
|
|
}
|
|
};
|
|
|
|
//Bushido ability todo:add bushido count.
|
|
class ABushidoAbility: public MTGAbility
|
|
{
|
|
public:
|
|
string PowerToughnessModifier;
|
|
|
|
ABushidoAbility(GameObserver* observer, int _id, MTGCardInstance * _source, string _PowerToughnessModifier) :
|
|
MTGAbility(observer, _id, _source)
|
|
{
|
|
PowerToughnessModifier = _PowerToughnessModifier;
|
|
}
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
if (dynamic_cast<WEventBlockersChosen*> (event))
|
|
{
|
|
MTGCardInstance * opponent = source->getNextOpponent();
|
|
if (!opponent) return 0;
|
|
PTInstant * a = NEW PTInstant(game, this->GetId(), source, source,NEW WParsedPT(PowerToughnessModifier,NULL,source));
|
|
GenericInstantAbility * wrapper = NEW GenericInstantAbility(game, 1, source,source, a);
|
|
wrapper->addToGame();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
ABushidoAbility * clone() const
|
|
{
|
|
return NEW ABushidoAbility(*this);
|
|
}
|
|
};
|
|
|
|
class AACastCard: public MTGAbility
|
|
{
|
|
public:
|
|
MTGAbility * andAbility;
|
|
bool processed;
|
|
bool restricted;
|
|
bool asCopy;
|
|
bool normal;
|
|
string cardNamed;
|
|
string nameThis;
|
|
MTGCardInstance * theNamedCard;
|
|
bool noEvent;
|
|
bool putinplay;
|
|
bool asNormalMadness;
|
|
AACastCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target,bool restricted,bool copied,bool _asNormal,string nameCard,string abilityName,bool _noEvent, bool putinplay,bool asNormalMadness = false);
|
|
|
|
int testDestroy(){return 0;};
|
|
void Update(float dt);
|
|
const string getMenuText();
|
|
int isReactingToTargetClick(Targetable * card);
|
|
int reactToTargetClick(Targetable * object);
|
|
MTGCardInstance * makeCard();
|
|
int resolveSpell();
|
|
|
|
AACastCard * clone() const;
|
|
~AACastCard();
|
|
};
|
|
|
|
//A Spirit Link Ability
|
|
class ASpiritLinkAbility: public MTGAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * source;
|
|
bool combatonly;
|
|
ASpiritLinkAbility(GameObserver* observer, int _id, MTGCardInstance * _source,bool combatonly = false) :
|
|
MTGAbility(observer, _id, _source),source(_source),combatonly(combatonly)
|
|
{
|
|
}
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
if (event->type == WEvent::DAMAGE)
|
|
{
|
|
WEventDamage * e = (WEventDamage *) event;
|
|
Damage * d = e->damage;
|
|
if (combatonly && e->damage->typeOfDamage != Damage::DAMAGE_COMBAT)
|
|
return 0;
|
|
MTGCardInstance * card = d->source;
|
|
if (d->damage > 0 && card && (card == source || card == source->target))
|
|
{
|
|
source->owner->gainLife(d->damage);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
ASpiritLinkAbility * clone() const
|
|
{
|
|
return NEW ASpiritLinkAbility(*this);
|
|
}
|
|
};
|
|
|
|
//Instant Steal control of a target
|
|
class AInstantControlSteal: public InstantAbility
|
|
{
|
|
public:
|
|
Player * TrueController;
|
|
Player * TheftController;
|
|
AInstantControlSteal(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
InstantAbility(observer, _id, _source, _target)
|
|
{
|
|
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _theftTarget = (MTGCardInstance*)target;
|
|
bool recast = false;
|
|
if(!_theftTarget->isInPlay(game))
|
|
{
|
|
recast = true;
|
|
}
|
|
while(_theftTarget->next)
|
|
{
|
|
_theftTarget= _theftTarget->next;
|
|
}
|
|
if(_theftTarget)
|
|
{
|
|
TrueController = _theftTarget->controller();
|
|
TheftController = source->controller();
|
|
MTGCardInstance * copy = _theftTarget->changeController(TheftController);
|
|
target = copy;
|
|
source->target = copy;
|
|
copy->summoningSickness = 0;
|
|
if(recast)
|
|
{
|
|
Spell * spell = NEW Spell(game, copy);
|
|
spell->resolve();
|
|
SAFE_DELETE(spell);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (TheftController && TheftController->game->inPlay->hasCard(_target))
|
|
{ //if the target is still in game -> spell was destroyed
|
|
_target->changeController(TrueController);
|
|
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AInstantControlSteal ::: TrueController : " << TrueController << " ; TheftController : " << TheftController << " (";
|
|
return InstantAbility::toString(out) << ")";
|
|
}
|
|
|
|
AInstantControlSteal * clone() const
|
|
{
|
|
return NEW AInstantControlSteal(*this);
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------
|
|
class AASetColorChosen: public InstantAbility
|
|
{
|
|
public:
|
|
int color;
|
|
string abilityToAlter;
|
|
MTGAbility * abilityAltered;
|
|
AASetColorChosen(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, int _color = 0 ,string toAdd = "");
|
|
int resolve();
|
|
const string getMenuText();
|
|
AASetColorChosen * clone() const;
|
|
~AASetColorChosen();
|
|
};
|
|
class AASetTypeChosen: public InstantAbility
|
|
{
|
|
public:
|
|
int type;
|
|
string abilityToAlter;
|
|
string menutext;
|
|
MTGAbility * abilityAltered;
|
|
AASetTypeChosen(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, int _type = 0,string menu = "error" ,string toAdd = "");
|
|
int resolve();
|
|
const string getMenuText();
|
|
AASetTypeChosen * clone() const;
|
|
~AASetTypeChosen();
|
|
};
|
|
class GenericChooseTypeColor: public ActivatedAbility
|
|
{
|
|
public:
|
|
string baseAbility;
|
|
bool chooseColor;
|
|
AASetColorChosen * setColor;
|
|
AASetTypeChosen * setType;
|
|
bool ANonWall;
|
|
GenericChooseTypeColor(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target, string toAdd = "",bool chooseColor = false,bool nonwall = false, ManaCost * cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
GenericChooseTypeColor * clone() const;
|
|
~GenericChooseTypeColor();
|
|
|
|
};
|
|
//------------------------------------------------
|
|
//flip a coin and call it, with win or lose abilities
|
|
class AASetCoin: public InstantAbility
|
|
{
|
|
public:
|
|
int side;
|
|
string abilityToAlter;
|
|
string abilityWin;
|
|
string abilityLose;
|
|
MTGAbility * abilityAltered;
|
|
AASetCoin(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, int side = -1,string toAdd = "");
|
|
int resolve();
|
|
const string getMenuText();
|
|
AASetCoin * clone() const;
|
|
~AASetCoin();
|
|
};
|
|
class GenericFlipACoin: public ActivatedAbility
|
|
{
|
|
public:
|
|
string baseAbility;
|
|
AASetCoin * setCoin;
|
|
GenericFlipACoin(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target, string toAdd = "", ManaCost * cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
GenericFlipACoin * clone() const;
|
|
~GenericFlipACoin();
|
|
|
|
};
|
|
//------------
|
|
class GenericPaidAbility: public ActivatedAbility
|
|
{
|
|
public:
|
|
MTGAbility * baseAbility;
|
|
ManaCost * optionalCost;
|
|
string newName;
|
|
string restrictions;
|
|
string baseCost;
|
|
string baseAbilityStr;
|
|
bool asAlternate;
|
|
|
|
GenericPaidAbility(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target,string _newName,string _castRestriction,string _mayCost, string toAdd, bool asAlternate = false, ManaCost * cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
GenericPaidAbility * clone() const;
|
|
~GenericPaidAbility();
|
|
|
|
};
|
|
// utility functions
|
|
|
|
void PopulateColorIndexVector(list<int>& colors, const string& colorsString, char delimiter = ',');
|
|
void PopulateAbilityIndexVector(list<int>& abilities, const string& abilitiesString, char delimiter = ',');
|
|
void PopulateSubtypesIndexVector(list<int>& types, const string& subtypesString, char delimiter = ' ');
|
|
|
|
#endif
|