the alias is used for checking of "urzatron". revised this because when using if then else in the activation makes the mana ability goes to the stack
6356 lines
188 KiB
C++
6356 lines
188 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 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)
|
|
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 plusone = false;
|
|
bool plustwo = false;
|
|
bool plusthree = 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("plusone") != string::npos)
|
|
{
|
|
plusone = true;
|
|
size_t pOne = s.find("plusone");
|
|
s.erase(pOne,pOne + 7);
|
|
}
|
|
if(s.find("plustwo") != string::npos)
|
|
{
|
|
plustwo = true;
|
|
size_t pTwo = s.find("plustwo");
|
|
s.erase(pTwo,pTwo + 7);
|
|
}
|
|
if(s.find("plusthree") != string::npos)
|
|
{
|
|
plusthree = true;
|
|
size_t pThree = s.find("plusthree");
|
|
s.erase(pThree,pThree + 9);
|
|
}
|
|
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 == "prex")
|
|
{
|
|
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->getManaCost()->getConvertedCost() + target->castX;
|
|
else
|
|
intValue = target->getManaCost()->getConvertedCost();
|
|
}
|
|
else if (s == "azorius")//devotion blue white
|
|
{
|
|
TargetChooserFactory dtf(card->getObserver());
|
|
TargetChooser * dtc = dtf.createTargetChooser("*",NULL);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * dp = card->getObserver()->players[i];
|
|
MTGGameZone * dzones[] = { dp->game->inPlay, dp->game->graveyard, dp->game->hand, dp->game->library };
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
MTGGameZone * zone = dzones[k];
|
|
if (dtc->targetsZone(zone, card)&&dp == card->controller())
|
|
{
|
|
intValue += zone->countDevotion(dtc, 2);
|
|
intValue += zone->countDevotion(dtc, 5);
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(dtc);
|
|
}
|
|
else if (s == "boros")//devotion red white
|
|
{
|
|
TargetChooserFactory dtf(card->getObserver());
|
|
TargetChooser * dtc = dtf.createTargetChooser("*",NULL);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * dp = card->getObserver()->players[i];
|
|
MTGGameZone * dzones[] = { dp->game->inPlay, dp->game->graveyard, dp->game->hand, dp->game->library };
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
MTGGameZone * zone = dzones[k];
|
|
if (dtc->targetsZone(zone, card)&&dp == card->controller())
|
|
{
|
|
intValue += zone->countDevotion(dtc, 3);
|
|
intValue += zone->countDevotion(dtc, 5);
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(dtc);
|
|
}
|
|
else if (s == "dimir")//devotion blue black
|
|
{
|
|
TargetChooserFactory dtf(card->getObserver());
|
|
TargetChooser * dtc = dtf.createTargetChooser("*",NULL);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * dp = card->getObserver()->players[i];
|
|
MTGGameZone * dzones[] = { dp->game->inPlay, dp->game->graveyard, dp->game->hand, dp->game->library };
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
MTGGameZone * zone = dzones[k];
|
|
if (dtc->targetsZone(zone, card)&&dp == card->controller())
|
|
{
|
|
intValue += zone->countDevotion(dtc, 2);
|
|
intValue += zone->countDevotion(dtc, 4);
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(dtc);
|
|
}
|
|
else if (s == "golgari")//devotion to green black
|
|
{
|
|
TargetChooserFactory dtf(card->getObserver());
|
|
TargetChooser * dtc = dtf.createTargetChooser("*",NULL);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * dp = card->getObserver()->players[i];
|
|
MTGGameZone * dzones[] = { dp->game->inPlay, dp->game->graveyard, dp->game->hand, dp->game->library };
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
MTGGameZone * zone = dzones[k];
|
|
if (dtc->targetsZone(zone, card)&&dp == card->controller())
|
|
{
|
|
intValue += zone->countDevotion(dtc, 1);
|
|
intValue += zone->countDevotion(dtc, 4);
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(dtc);
|
|
}
|
|
else if (s == "gruul")//devotion to green red
|
|
{
|
|
TargetChooserFactory dtf(card->getObserver());
|
|
TargetChooser * dtc = dtf.createTargetChooser("*",NULL);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * dp = card->getObserver()->players[i];
|
|
MTGGameZone * dzones[] = { dp->game->inPlay, dp->game->graveyard, dp->game->hand, dp->game->library };
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
MTGGameZone * zone = dzones[k];
|
|
if (dtc->targetsZone(zone, card)&&dp == card->controller())
|
|
{
|
|
intValue += zone->countDevotion(dtc, 1);
|
|
intValue += zone->countDevotion(dtc, 3);
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(dtc);
|
|
}
|
|
else if (s == "izzet")//devotion to red blue
|
|
{
|
|
TargetChooserFactory dtf(card->getObserver());
|
|
TargetChooser * dtc = dtf.createTargetChooser("*",NULL);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * dp = card->getObserver()->players[i];
|
|
MTGGameZone * dzones[] = { dp->game->inPlay, dp->game->graveyard, dp->game->hand, dp->game->library };
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
MTGGameZone * zone = dzones[k];
|
|
if (dtc->targetsZone(zone, card)&&dp == card->controller())
|
|
{
|
|
intValue += zone->countDevotion(dtc, 2);
|
|
intValue += zone->countDevotion(dtc, 3);
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(dtc);
|
|
}
|
|
else if (s == "orzhov")//devotion to white black
|
|
{
|
|
TargetChooserFactory dtf(card->getObserver());
|
|
TargetChooser * dtc = dtf.createTargetChooser("*",NULL);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * dp = card->getObserver()->players[i];
|
|
MTGGameZone * dzones[] = { dp->game->inPlay, dp->game->graveyard, dp->game->hand, dp->game->library };
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
MTGGameZone * zone = dzones[k];
|
|
if (dtc->targetsZone(zone, card)&&dp == card->controller())
|
|
{
|
|
intValue += zone->countDevotion(dtc, 4);
|
|
intValue += zone->countDevotion(dtc, 5);
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(dtc);
|
|
}
|
|
else if (s == "rakdos")//devotion to red black
|
|
{
|
|
TargetChooserFactory dtf(card->getObserver());
|
|
TargetChooser * dtc = dtf.createTargetChooser("*",NULL);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * dp = card->getObserver()->players[i];
|
|
MTGGameZone * dzones[] = { dp->game->inPlay, dp->game->graveyard, dp->game->hand, dp->game->library };
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
MTGGameZone * zone = dzones[k];
|
|
if (dtc->targetsZone(zone, card)&&dp == card->controller())
|
|
{
|
|
intValue += zone->countDevotion(dtc, 3);
|
|
intValue += zone->countDevotion(dtc, 4);
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(dtc);
|
|
}
|
|
else if (s == "selesnya")//devotion to green white
|
|
{
|
|
TargetChooserFactory dtf(card->getObserver());
|
|
TargetChooser * dtc = dtf.createTargetChooser("*",NULL);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * dp = card->getObserver()->players[i];
|
|
MTGGameZone * dzones[] = { dp->game->inPlay, dp->game->graveyard, dp->game->hand, dp->game->library };
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
MTGGameZone * zone = dzones[k];
|
|
if (dtc->targetsZone(zone, card)&&dp == card->controller())
|
|
{
|
|
intValue += zone->countDevotion(dtc, 1);
|
|
intValue += zone->countDevotion(dtc, 5);
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(dtc);
|
|
}
|
|
else if (s == "simic")//devotion to green blue
|
|
{
|
|
TargetChooserFactory dtf(card->getObserver());
|
|
TargetChooser * dtc = dtf.createTargetChooser("*",NULL);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * dp = card->getObserver()->players[i];
|
|
MTGGameZone * dzones[] = { dp->game->inPlay, dp->game->graveyard, dp->game->hand, dp->game->library };
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
MTGGameZone * zone = dzones[k];
|
|
if (dtc->targetsZone(zone, card)&&dp == card->controller())
|
|
{
|
|
intValue += zone->countDevotion(dtc, 1);
|
|
intValue += zone->countDevotion(dtc, 2);
|
|
}
|
|
}
|
|
}
|
|
SAFE_DELETE(dtc);
|
|
}
|
|
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 };
|
|
for (int k = 0; k < 4; 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 };
|
|
for (int k = 0; k < 4; 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 == "targetedcurses")
|
|
{
|
|
if(card->playerTarget)
|
|
intValue = card->playerTarget->curses.size();
|
|
}
|
|
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 == "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 == "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 == "p" || s == "power")
|
|
{
|
|
intValue = target->getCurrentPower();
|
|
}
|
|
else if (s == "t" || s == "toughness")
|
|
{
|
|
intValue = target->getCurrentToughness();
|
|
}
|
|
else if (s == "kicked")
|
|
{
|
|
intValue = target->kicked;
|
|
}
|
|
else if (s == "handsize")
|
|
{
|
|
intValue = target->controller()->handsize;
|
|
}
|
|
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
|
|
{
|
|
intValue = 0;
|
|
for (int j = card->controller()->game->battlefield->nb_cards - 1; j >= 0; --j)
|
|
{
|
|
if (card->controller()->game->battlefield->cards[j]->hasType("forest"))
|
|
{
|
|
intValue += 1;
|
|
}
|
|
if (card->controller()->game->battlefield->cards[j]->hasType("treefolk"))
|
|
{
|
|
intValue += 1;
|
|
}
|
|
}
|
|
}
|
|
else if (s == "pbasiclandtypes")//Basic Land types
|
|
{
|
|
intValue = 0;
|
|
int forest = 0, plains = 0, swamp = 0, island = 0, mountain = 0;
|
|
for (int j = card->controller()->game->battlefield->nb_cards - 1; j >= 0; --j)
|
|
{
|
|
if (card->controller()->game->battlefield->cards[j]->hasType("forest"))
|
|
{
|
|
forest = 1;
|
|
}
|
|
if (card->controller()->game->battlefield->cards[j]->hasType("plains"))
|
|
{
|
|
plains = 1;
|
|
}
|
|
if (card->controller()->game->battlefield->cards[j]->hasType("swamp"))
|
|
{
|
|
swamp = 1;
|
|
}
|
|
if (card->controller()->game->battlefield->cards[j]->hasType("island"))
|
|
{
|
|
island = 1;
|
|
}
|
|
if (card->controller()->game->battlefield->cards[j]->hasType("mountain"))
|
|
{
|
|
mountain = 1;
|
|
}
|
|
}
|
|
intValue = mountain + island + forest + swamp + plains;
|
|
}
|
|
else if (s == "myname")//Name of the card you control
|
|
{
|
|
intValue = 0;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * p = card->getObserver()->players[i];
|
|
for (int j = p->game->battlefield->nb_cards - 1; j >= 0; --j)
|
|
{
|
|
if (p->game->battlefield->cards[j]->name == card->name && p == card->controller())
|
|
{
|
|
intValue += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (s == "allmyname")//Plague Rats and others
|
|
{
|
|
intValue = 0;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * p = card->getObserver()->players[i];
|
|
for (int j = p->game->battlefield->nb_cards - 1; j >= 0; --j)
|
|
{
|
|
if (p->game->battlefield->cards[j]->name == card->name)
|
|
{
|
|
intValue += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (s == "pgbzombie")//Soulless One
|
|
{
|
|
intValue = 0;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * p = card->getObserver()->players[i];
|
|
for (int j = p->game->graveyard->nb_cards - 1; j >= 0; --j)
|
|
{
|
|
if (p->game->graveyard->cards[j]->hasType("zombie"))
|
|
{
|
|
intValue += 1;
|
|
}
|
|
}
|
|
for (int j = p->game->inPlay->nb_cards - 1; j >= 0; --j)
|
|
{
|
|
if (p->game->inPlay->cards[j]->hasType("zombie"))
|
|
{
|
|
intValue += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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
|
|
{
|
|
intValue = 0;
|
|
int art = 0, cre = 0, enc = 0, ins = 0, lnd = 0, sor = 0, trb = 0, pwk = 0;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Player * p = card->getObserver()->players[i];
|
|
if(p->game->graveyard->hasType("planeswalker"))
|
|
pwk = 1;
|
|
if(p->game->graveyard->hasType("tribal"))
|
|
trb = 1;
|
|
if(p->game->graveyard->hasType("sorcery"))
|
|
sor = 1;
|
|
if(p->game->graveyard->hasType("land"))
|
|
lnd = 1;
|
|
if(p->game->graveyard->hasType("instant"))
|
|
ins = 1;
|
|
if(p->game->graveyard->hasType("enchantment"))
|
|
enc = 1;
|
|
if(p->game->graveyard->hasType("creature"))
|
|
cre = 1;
|
|
if(p->game->graveyard->hasType("artifact"))
|
|
art = 1;
|
|
}
|
|
intValue = art + cre + enc + ins + lnd + sor + trb + pwk;
|
|
}
|
|
else if (s == "morethanfourcards")
|
|
{
|
|
if(card->playerTarget)
|
|
{//blackvise
|
|
intValue = 0;
|
|
if ((card->playerTarget->game->hand->nb_cards - 4)>0)
|
|
intValue = (card->playerTarget->game->hand->nb_cards - 4);
|
|
}
|
|
else
|
|
{//viseling
|
|
intValue = 0;
|
|
if ((card->controller()->opponent()->game->hand->nb_cards - 4)>0)
|
|
intValue = (card->controller()->opponent()->game->hand->nb_cards - 4);
|
|
}
|
|
}
|
|
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
|
|
{
|
|
intValue = atoi(s.c_str());
|
|
}
|
|
if(intValue > 0)
|
|
{
|
|
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(plusone)
|
|
intValue = intValue+1;
|
|
if(plustwo)
|
|
intValue = intValue+2;
|
|
if(plusthree)
|
|
intValue = intValue+3;
|
|
}
|
|
else
|
|
{
|
|
if(plusone)
|
|
intValue = intValue+1;
|
|
if(plustwo)
|
|
intValue = intValue+2;
|
|
if(plusthree)
|
|
intValue = intValue+3;
|
|
}
|
|
intValue *= multiplier;
|
|
}
|
|
public:
|
|
|
|
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->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:
|
|
|
|
TrcardDrawn(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc,bool once = false) :
|
|
Trigger(observer, id, source,once, tc)
|
|
{
|
|
}
|
|
|
|
int triggerOnEventImpl(WEvent * event)
|
|
{
|
|
WEventcardDraw * e = dynamic_cast<WEventcardDraw *> (event);
|
|
if (!e) return 0;
|
|
if (!tc->canTarget(e->player)) 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;
|
|
check->currentZone = check->previousZone;
|
|
if (check->next && check->next->currentZone)
|
|
{
|
|
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;
|
|
TrDamaged(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL, int type = 0,bool sourceUntapped = false,bool limitOnceATurn = false,bool once = false) :
|
|
Trigger(observer, id, source, once, tc), fromTc(fromTc), type(type) , sourceUntapped(sourceUntapped),limitOnceATurn(limitOnceATurn)
|
|
{
|
|
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;
|
|
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;
|
|
TrLifeGained(GameObserver* observer, int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL, int type = 0,bool sourceUntapped = false,bool once = false) :
|
|
Trigger(observer, id, source, once , tc), fromTc(fromTc), type(type) , sourceUntapped(sourceUntapped)
|
|
{
|
|
}
|
|
|
|
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;
|
|
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);
|
|
}
|
|
};
|
|
|
|
//targetted 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;
|
|
AAEPIC(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target,string _newName, ManaCost * cost = NULL);
|
|
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;
|
|
};
|
|
//imprint
|
|
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;
|
|
};
|
|
//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;
|
|
AAMover(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, string dest,string _name, ManaCost * _cost = NULL);
|
|
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);
|
|
|
|
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);
|
|
_target->modifiedbAbi += 1;
|
|
return InstantAbility::addToGame();
|
|
}
|
|
|
|
const string getMenuText()
|
|
{
|
|
return Constants::MTGBasicAbilities[ability];
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
_target->basicAbilities.set(ability, stateBeforeActivation);
|
|
_target->modifiedbAbi -= 1;
|
|
}
|
|
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);
|
|
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(!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())
|
|
{
|
|
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 };
|
|
for (int k = 0; k < 4; 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->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
|
|
{
|
|
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 name;
|
|
string sabilities;
|
|
string starfound;
|
|
WParsedInt * multiplier;
|
|
int who;
|
|
bool aLivingWeapon;
|
|
string spt;
|
|
bool battleReady;
|
|
MTGCardInstance * myToken;
|
|
vector<MTGAbility *> currentAbilities;
|
|
Player * tokenReciever;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
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);
|
|
}
|
|
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;
|
|
AThis(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;
|
|
a = NULL;
|
|
SAFE_DELETE(tc);
|
|
}
|
|
|
|
int removeFromGame()
|
|
{
|
|
return removeAbilityFromGame();
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
void Update(float)
|
|
{
|
|
resolve();
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
//TODO check if ability is oneShot ?
|
|
int match;
|
|
match = td->match(source);
|
|
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();
|
|
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;
|
|
}
|
|
};
|
|
|
|
//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);
|
|
}
|
|
};
|
|
|
|
/* Can tap a target for a cost */
|
|
class AATapper: public ActivatedAbility
|
|
{
|
|
public:
|
|
AATapper(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL);
|
|
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;
|
|
};
|
|
|
|
/* 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;
|
|
};
|
|
|
|
/* Can prevent a card from untapping next untap */
|
|
class AAFrozen: public ActivatedAbility
|
|
{
|
|
public:
|
|
AAFrozen(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AAFrozen * clone() const;
|
|
};
|
|
/* ghetto new target*/
|
|
class AANewTarget: public ActivatedAbility
|
|
{
|
|
public:
|
|
bool retarget;
|
|
AANewTarget(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,bool retarget = false, ManaCost * _cost = NULL);
|
|
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;
|
|
};
|
|
/* flip*/
|
|
class AAFlip: public InstantAbility
|
|
{
|
|
public:
|
|
vector<MTGAbility *> currentAbilities;
|
|
string flipStats;
|
|
AAFlip(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target,string flipStats);
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
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;
|
|
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);
|
|
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);
|
|
int resolve();
|
|
const string getMenuText();
|
|
APhaseActionGeneric * clone() const;
|
|
~APhaseActionGeneric();
|
|
|
|
};
|
|
|
|
//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_INSTANT)) && !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;
|
|
AADepleter(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbcardsStr, ManaCost * _cost = NULL,
|
|
int who = TargetChooser::UNSET, bool toexile = false);
|
|
int resolve();
|
|
const string getMenuText();
|
|
AADepleter * 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->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;
|
|
AACastCard(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target,bool restricted,bool copied,bool _asNormal,string nameCard,string abilityName,bool _noEvent, bool putinplay);
|
|
|
|
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;
|
|
|
|
GenericPaidAbility(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target,string _newName,string _castRestriction,string _mayCost, string toAdd, 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
|