Files
wagic/projects/mtg/include/AllAbilities.h
wagic.the.homebrew@gmail.com a3cbbedd3c Erwan
- fix issue 392 (broken tests)
- Fix a bunch of memory leaks (guys please be careful!)
- Added Logging facility in JGE
- HBL Compatibility (cleaned up some code with MP3 in JGE)
- Added "winGame" ability. Currently used mostly by the story mode, but some cards could probably need it too
- Improved story mode and uncommented it from the source.
-- The current campaign is of course very basic, anybody who wants to improve it or create other ones feel free to do so
-- TODO (short term): save progress, rewards system, improve tutorial campaign
-- I'll talk a bit more about this on the forums/email after a night of sleep
2010-04-26 14:27:34 +00:00

4050 lines
106 KiB
C++

#ifndef _CARDS_H_
#define _CARDS_H_
#include "MTGAbility.h"
#include "ManaCost.h"
#include "CardDescriptor.h"
#include "AIPlayer.h"
#include "CardDisplay.h"
#include "Subtypes.h"
#include "CardGui.h"
#include "GameOptions.h"
#include "Token.h"
#include "Counters.h"
#include "WEvent.h"
#include "GuiStatic.h"
#include "GameObserver.h"
#include "ThisDescriptor.h"
#include <JGui.h>
#include <hge/hgeparticle.h>
#include <map>
using std::map;
//
// Misc classes
//
class WParsedInt{
public:
int intValue;
int computeX(Spell * spell, MTGCardInstance * card){
if (spell) return spell->computeX(card);
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)
}
WParsedInt(int value = 0){
intValue = value;
}
WParsedInt(string s, Spell * spell, MTGCardInstance * card){
MTGCardInstance * target = card->target;
if (!target) target = card;
int multiplier = 1;
if (s[0] == '-'){
s = s.substr(1);
multiplier = -1;
}
if (s == "x" || s == "X"){
intValue = computeX(spell,card);
}else if (s == "manacost"){
intValue = target->getManaCost()->getConvertedCost();
}else if (s == "p"){
intValue = target->power;
}else if (s == "t"){
intValue = target->toughness;
}else{
intValue = atoi(s.c_str());
}
intValue *= multiplier;
}
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
//
class TrCardAddedToZone:public TriggeredAbility{
public:
TargetChooser * toTc;
TargetZoneChooser * fromTcZone;
TargetChooser * fromTcCard;
TrCardAddedToZone(int id, MTGCardInstance * source, TargetChooser * toTc, TargetZoneChooser * fromTcZone = NULL,TargetChooser * fromTcCard = NULL):TriggeredAbility(id,source), toTc(toTc), fromTcZone(fromTcZone), fromTcCard(fromTcCard){}
int resolve(){
return 0; //This is a trigger, this function should not be called
}
int triggerOnEvent(WEvent * event){
WEventZoneChange * e = dynamic_cast<WEventZoneChange*>(event);
if (!e) return 0;
if (!toTc->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(toTc);
SAFE_DELETE(fromTcZone);
SAFE_DELETE(fromTcCard);
}
TrCardAddedToZone * clone() const{
TrCardAddedToZone * a = NEW TrCardAddedToZone(*this);
a->isClone = 1;
return a;
}
};
class TrCardTapped:public TriggeredAbility{
public:
TargetChooser * tc;
bool tap;
TrCardTapped(int id, MTGCardInstance * source, TargetChooser * tc, bool tap = true):TriggeredAbility(id,source), tc(tc),tap(tap){}
int resolve(){
return 0; //This is a trigger, this function should not be called
}
int triggerOnEvent(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(){
SAFE_DELETE(tc);
}
TrCardTapped * clone() const{
TrCardTapped * a = NEW TrCardTapped(*this);
a->isClone = 1;
return a;
}
};
class TrDamaged:public TriggeredAbility{
public:
TargetChooser * tc;
TargetChooser * fromTc;
TrDamaged (int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL):TriggeredAbility(id,source), tc(tc), fromTc(fromTc){}
int resolve(){
return 0; //This is a trigger, this function should not be called
}
int triggerOnEvent(WEvent * event){
WEventDamage * e = dynamic_cast<WEventDamage *>(event);
if (!e) return 0;
if(!tc->canTarget(e->damage->target)) return 0;
if (fromTc && !fromTc->canTarget(e->damage->source)) return 0;
return 1;
}
~TrDamaged (){
SAFE_DELETE(tc);
SAFE_DELETE(fromTc);
}
TrDamaged * clone() const{
TrDamaged * a = NEW TrDamaged (*this);
a->isClone = 1;
return a;
}
};
//counters
class AACounter: public ActivatedAbility{
public:
int nb;
int power;
int toughness;
string name;
AACounter(int id, MTGCardInstance * source, MTGCardInstance * target, const char * _name, int power, int toughness, int nb, ManaCost * cost = NULL, int doTap = 0) : ActivatedAbility(id, source, cost, 0, doTap), nb(nb), power(power), toughness(toughness), name(_name) {
this->target = target;
}
int resolve(){
if (target){
MTGCardInstance * _target = (MTGCardInstance *)target;
if (nb>0){
for (int i=0; i < nb; i++){
_target->counters->addCounter(name.c_str(), power, toughness);
}
}else{
for (int i=0; i < -nb; i++){
_target->counters->removeCounter(name.c_str(), power, toughness);
}
}
return nb;
}
return 0;
}
const char* getMenuText() {
return "Counter";
}
AACounter * clone() const{
AACounter * a = NEW AACounter(*this);
a->isClone = 1;
return a;
}
};
class AAFizzler:public ActivatedAbility{
public:
AAFizzler(int _id, MTGCardInstance * card, Spell * _target, ManaCost * _cost = NULL, int _tap = 0):ActivatedAbility(_id, card,_cost,0,_tap){
target = _target;
}
int resolve(){
Spell * _target = (Spell *) target;
if(target && _target->source->has(Constants::NOFIZZLE))
return 0;
game->mLayers->stackLayer()->Fizzle(_target);
return 1;
}
const char * getMenuText(){
return "Fizzle";
}
AAFizzler* clone() const{
AAFizzler * a = NEW AAFizzler(*this);
a->isClone = 1;
return a;
}
};
/*
Generic classes
*/
//MayAbility: May do ...
class MayAbility:public MTGAbility, public NestedAbility{
public:
int triggered;
bool must;
MTGAbility * mClone;
MayAbility(int _id, MTGAbility * _ability, MTGCardInstance * _source, bool must = false):MTGAbility(_id,_source),NestedAbility(_ability),must(must){
triggered = 0;
mClone = NULL;
}
void Update(float dt){
MTGAbility::Update(dt);
if (!triggered){
triggered = 1;
if (TargetAbility * ta = dynamic_cast<TargetAbility *>(ability)){
if (!ta->tc->validTargetsExist()) return;
}
game->mLayers->actionLayer()->setMenuObject(source,must);
game->mLayers->stackLayer()->setIsInterrupting(source->controller());
OutputDebugString("ALLABILITIES SetMenuObject!\n");
}
}
const char * getMenuText(){
return ability->getMenuText();
}
int testDestroy(){
if (!triggered) return 0;
if (game->mLayers->actionLayer()->menuObject) return 0;
if (game->mLayers->actionLayer()->getIndexOf(mClone) !=-1) return 0;
return 1;
}
int isReactingToTargetClick(Targetable * card){
if (card == source) return 1;
return 0;
}
int reactToTargetClick(Targetable * object){
mClone = ability->clone();
mClone->addToGame();
mClone->forceDestroy = 1;
return mClone->reactToTargetClick(object);
}
~MayAbility(){
SAFE_DELETE(ability);
}
MayAbility * clone() const{
MayAbility * a = NEW MayAbility(*this);
a->ability = ability->clone();
a->isClone = 1;
return a;
}
};
//MultiAbility : triggers several actions for a cost
class MultiAbility:public ActivatedAbility{
public:
vector<MTGAbility *> abilities;
MultiAbility(int _id, MTGCardInstance * card,Targetable * _target, ManaCost * _cost, int _tap):ActivatedAbility(_id, card,_cost,0,_tap){
if (_target) target = _target;
}
int Add(MTGAbility * ability){
abilities.push_back(ability);
return 1;
}
int resolve(){
vector<int>::size_type sz = abilities.size();
for (unsigned int i = 0; i < sz; i++){
Targetable * backup = abilities[i]->target;
if (target && target!= source && abilities[i]->target == abilities[i]->source) abilities[i]->target = target;
abilities[i]->resolve();
abilities[i]->target = backup;
}
return 1;
}
~MultiAbility(){
if (!isClone){
vector<int>::size_type sz = abilities.size();
for (size_t i = 0; i < sz; i++){
delete abilities[i];
}
}
abilities.clear();
}
const char * getMenuText(){
if (abilities.size()) return abilities[0]->getMenuText();
return "";
}
MultiAbility * clone() const{
MultiAbility * a = NEW MultiAbility(*this);
a->isClone = 1;
return a;
}
};
//Generic Activated Ability
class GenericActivatedAbility:public ActivatedAbility, public NestedAbility{
public:
int limitPerTurn;
int counters;
MTGGameZone * activeZone;
GenericActivatedAbility(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int _tap = 0, int limit = 0, int restrictions = 0, MTGGameZone * dest = NULL):ActivatedAbility(_id, card,_cost,restrictions,_tap),NestedAbility(a),limitPerTurn(limit),activeZone(dest){
counters = 0;
target = ability->target;
}
int resolve(){
counters++;
ManaCost * diff = abilityCost->Diff(cost);
source->X = diff->hasX();
SAFE_DELETE(diff);
SAFE_DELETE(abilityCost);
ability->target = target; //may have been updated...
if (ability) return ability->resolve();
return 0;
}
const char * getMenuText(){
if (ability) return ability->getMenuText();
return "Error";
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
if (limitPerTurn && counters >= limitPerTurn) return 0;
return ActivatedAbility::isReactingToClick(card,mana);
}
void Update(float dt){
if (newPhase != currentPhase && newPhase ==Constants::MTG_PHASE_AFTER_EOT){
counters = 0;
}
ActivatedAbility::Update(dt);
}
GenericActivatedAbility * clone() const{
GenericActivatedAbility * a = NEW GenericActivatedAbility(*this);
a->cost = NEW ManaCost();
a->cost->copy(cost);
a->ability = ability->clone();
return a;
}
~GenericActivatedAbility(){
SAFE_DELETE(ability);
}
int testDestroy(){
if (!activeZone) return ActivatedAbility::testDestroy();
if (activeZone->hasCard(source)) return 0;
return 1;
}
};
//Copier. ActivatedAbility
class AACopier:public ActivatedAbility{
public:
AACopier(int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost=NULL):ActivatedAbility(_id,_source,_cost,0,0){
target = _target;
}
int resolve(){
MTGCardInstance * _target = (MTGCardInstance *) target;
if(_target){
source->copy(_target);
return 1;
}
return 0;
}
const char * getMenuText(){
return "Copy";
}
AACopier * clone() const{
AACopier * a = NEW AACopier(*this);
a->isClone = 1;
return a;
}
};
/* Generic TargetAbility */
class GenericTargetAbility:public TargetAbility{
public:
int limitPerTurn;
int counters;
MTGGameZone * activeZone;
GenericTargetAbility(int _id, MTGCardInstance * _source, TargetChooser * _tc,MTGAbility * a, ManaCost * _cost = NULL, int _tap=0, int limit = 0, int restrictions = 0, MTGGameZone * dest = NULL):TargetAbility(_id,_source, _tc,_cost,restrictions,_tap),limitPerTurn(limit), activeZone(dest){
ability = a;
MTGAbility * core = AbilityFactory::getCoreAbility(a);
if (dynamic_cast<AACopier *>(core)) tc->other = true; //http://code.google.com/p/wagic/issues/detail?id=209 (avoid inifinite loop)
counters = 0;
}
~GenericTargetAbility(){
SAFE_DELETE(ability);
}
GenericTargetAbility * clone() const{
GenericTargetAbility * a = NEW GenericTargetAbility(*this);
a->ability = ability->clone();
a->cost = NEW ManaCost();
a->cost->copy(cost);
if (tc) a->tc = tc->clone();
return a;
}
int resolve(){
counters++;
return TargetAbility::resolve();
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
if (limitPerTurn && counters >= limitPerTurn) return 0;
return TargetAbility::isReactingToClick(card,mana);
}
void Update(float dt){
if (newPhase != currentPhase && newPhase ==Constants::MTG_PHASE_AFTER_EOT){
counters = 0;
}
TargetAbility::Update(dt);
}
int testDestroy(){
if (!activeZone) return TargetAbility::testDestroy();
if (activeZone->hasCard(source)) return 0;
return 1;
}
};
//Cycling
class ACycle:public ActivatedAbility{
public:
ACycle(int _id, MTGCardInstance * card,Targetable * _target):ActivatedAbility(_id, card){
target = _target;
}
int resolve(){
source->controller()->game->putInGraveyard(source);
source->controller()->game->drawFromLibrary();
return 1;
}
const char * getMenuText(){
return "Cycling";
}
ACycle * clone() const{
ACycle * a = NEW ACycle(*this);
a->isClone = 1;
return a;
}
};
//Drawer, allows to draw a card for a cost:
class AADrawer:public ActivatedAbilityTP{
public:
WParsedInt *nbcards;
AADrawer(int _id, MTGCardInstance * card,Targetable * _target,ManaCost * _cost, WParsedInt * _nbcards, int _tap = 0, int who=TargetChooser::UNSET):ActivatedAbilityTP(_id, card,_target,_cost,_tap,who),nbcards(_nbcards){
}
int resolve(){
Targetable * _target = getTarget();
Player * player;
if (_target){
if (_target->typeAsTarget() == TARGET_CARD){
player = ((MTGCardInstance *)_target)->controller();
}else{
player = (Player *) _target;
}
game->mLayers->stackLayer()->addDraw(player,nbcards->getValue());
game->mLayers->stackLayer()->resolve();
}
return 1;
}
const char * getMenuText(){
return "Draw";
}
AADrawer * clone() const{
AADrawer * a = NEW AADrawer(*this);
a->nbcards = NEW WParsedInt(*(a->nbcards));
a->isClone = 1;
return a;
}
~AADrawer(){
SAFE_DELETE(nbcards);
}
};
/*Gives life to target controller*/
class AALifer:public ActivatedAbilityTP{
public:
WParsedInt *life;
AALifer(int _id, MTGCardInstance * card, Targetable * _target, WParsedInt * life, ManaCost * _cost = NULL, int _tap = 0, int who = TargetChooser::UNSET):ActivatedAbilityTP(_id, card,_target,_cost,_tap,who),life(life){
}
int resolve(){
Damageable * _target = (Damageable *) getTarget();
if (_target){
if (_target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE){
_target = ((MTGCardInstance *)_target)->controller();
}
_target->life+=life->getValue();
}
return 1;
}
const char * getMenuText(){
return "Life";
}
AALifer * clone() const{
AALifer * a = NEW AALifer(*this);
a->life = NEW WParsedInt(*(a->life));
a->isClone = 1;
return a;
}
~AALifer(){
SAFE_DELETE(life);
}
};
/*Player Wins Game*/
class AAWinGame:public ActivatedAbilityTP{
public:
AAWinGame(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int _tap = 0, int who = TargetChooser::UNSET):ActivatedAbilityTP(_id, card,_target,_cost,_tap,who){
}
int resolve(){
Damageable * _target = (Damageable *) getTarget();
if (_target){
if (_target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE){
_target = ((MTGCardInstance *)_target)->controller();
}
game->gameOver = ((Player *)_target)->opponent();
}
return 1;
}
const char * getMenuText(){
return "Win Game";
}
AAWinGame * clone() const{
AAWinGame * a = NEW AAWinGame(*this);
a->isClone = 1;
return a;
}
};
class ATokenCreator:public ActivatedAbility{
public:
list<int>abilities;
list<int>types;
list<int>colors;
int power, toughness;
int tokenId;
string name;
WParsedInt * multiplier;
ATokenCreator(int _id,MTGCardInstance * _source,ManaCost * _cost, int tokenId, int _doTap, WParsedInt * multiplier = NULL):ActivatedAbility(_id,_source,_cost,0,_doTap), tokenId(tokenId), multiplier(multiplier){
if(!multiplier) this->multiplier = NEW WParsedInt(1);
MTGCard * card = GameApp::collection->getCardById(tokenId);
if (card) name = card->data->getName();
}
ATokenCreator(int _id,MTGCardInstance * _source,ManaCost * _cost, string sname, string stypes,int _power,int _toughness, string sabilities, int _doTap, WParsedInt * multiplier = NULL):ActivatedAbility(_id,_source,_cost,0,_doTap), multiplier(multiplier){
power = _power;
toughness = _toughness;
name = sname;
tokenId = 0;
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);
}
}
for (int j = 0; j < Constants::MTG_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){
int id = Subtypes::subtypesList->find(s.substr(0,found));
types.push_back(id);
s = s.substr(found+1);
}else{
int id = Subtypes::subtypesList->find(s);
types.push_back(id);
s = "";
}
}
}
int resolve(){
for (int i = 0; i < multiplier->getValue(); ++i){
MTGCardInstance * myToken;
if (tokenId){
MTGCard * card = GameApp::collection->getCardById(tokenId);
myToken = NEW MTGCardInstance(card,source->controller()->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;
}
}
source->controller()->game->temp->addCard(myToken);
Spell * spell = NEW Spell(myToken);
spell->resolve();
spell->source->isToken = 1;
delete spell;
}
return 1;
}
const char * 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
<< " (";
return ActivatedAbility::toString(out) << ")";
}
ATokenCreator * clone() const{
ATokenCreator * a = NEW ATokenCreator(*this);
a->isClone = 1;
return a;
}
~ATokenCreator(){
if (!isClone){
delete(multiplier);
}
}
};
class AAMover:public ActivatedAbility{
public:
string destination;
AAMover(int _id, MTGCardInstance * _source, MTGCardInstance * _target, string dest, ManaCost * _cost=NULL, int doTap=0):ActivatedAbility(_id,_source,_cost,0,doTap),destination(dest){
if (_target) target = _target;
}
MTGGameZone * destinationZone(){
MTGCardInstance * _target = (MTGCardInstance *) target;
return MTGGameZone::stringToZone(destination, source,_target);
}
int resolve(){
MTGCardInstance * _target = (MTGCardInstance *) target;
if(target){
Player* p = _target->controller();
if (p){
GameObserver * g = GameObserver::GetInstance();
MTGGameZone * fromZone = _target->getCurrentZone();
MTGGameZone * destZone = destinationZone();
//inplay is a special zone !
for (int i=0; i < 2; i++){
if (destZone == g->players[i]->game->inPlay && fromZone != g->players[i]->game->inPlay && fromZone != g->players[i]->opponent()->game->inPlay){
MTGCardInstance * copy = g->players[i]->game->putInZone(_target, fromZone, g->players[i]->game->temp);
Spell * spell = NEW Spell(copy);
spell->resolve();
delete spell;
return 1;
}
}
p->game->putInZone(_target,fromZone,destZone);
return 1;
}
}
return 0;
}
const char * getMenuText(){
return "Move";
}
AAMover * clone() const{
AAMover * a = NEW AAMover(*this);
a->isClone = 1;
return a;
}
};
class AADestroyer:public ActivatedAbility{
public:
int bury;
AADestroyer(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _bury = 0, ManaCost * _cost=NULL):ActivatedAbility(_id,_source,_cost),bury(_bury){
if (_target) target = _target;
}
int resolve(){
MTGCardInstance * _target = (MTGCardInstance *) target;
if(_target){
if (bury) return _target->bury();
else return _target->destroy();
}
return 0;
}
const char * getMenuText(){
return "Destroy";
}
AADestroyer * clone() const{
AADestroyer * a = NEW AADestroyer(*this);
a->isClone = 1;
return a;
}
};
/*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;
int value_before_modification;
ABasicAbilityModifier(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _ability, int _modifier = 1): MTGAbility(_id,_source,_target),modifier(_modifier),ability(_ability){
}
int addToGame(){
value_before_modification = ((MTGCardInstance * )target)->basicAbilities[ability];
((MTGCardInstance * )target)->basicAbilities[ability]=modifier;
return MTGAbility::addToGame();
}
int destroy(){
if (((MTGCardInstance * )target)->basicAbilities[ability] == modifier){
((MTGCardInstance * )target)->basicAbilities[ability] = value_before_modification;
return 1;
}else{
//BUG !!!
return 0;
}
}
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{
ABasicAbilityModifier * a = NEW ABasicAbilityModifier(*this);
a->isClone = 1;
return a;
}
};
//Modifies an ability until end of turn. Needs a target
class ABasicAbilityModifierUntilEOT:public TargetAbility{
public:
MTGCardInstance * mTargets[50];
int nbTargets;
int modifier;
int stateBeforeActivation[50];
int ability;
ABasicAbilityModifierUntilEOT(int _id, MTGCardInstance * _source, int _ability, ManaCost * _cost, TargetChooser * _tc = NULL, int _modifier = 1,int _tap=1): TargetAbility(_id,_source,_cost,0,_tap),modifier(_modifier), ability(_ability){
nbTargets = 0;
tc = _tc;
if (!tc) tc = NEW CreatureTargetChooser(_source);
}
void Update(float dt){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UNTAP){
for (int i = 0; i < nbTargets; i++){
MTGCardInstance * mTarget = mTargets[i];
if(mTarget && mTarget->basicAbilities[ability]){
mTarget->basicAbilities[ability] = stateBeforeActivation[i];
}
}
nbTargets = 0;
}
TargetAbility::Update(dt);
}
int resolve(){
MTGCardInstance * mTarget = tc->getNextCardTarget();
if (mTarget){
mTargets[nbTargets] = mTarget;
stateBeforeActivation[nbTargets] = mTarget->basicAbilities[ability];
mTarget->basicAbilities[ability] = modifier;
nbTargets++;
}
return 1;
}
int addToGame(){
resolve();
return ActivatedAbility::addToGame();
}
const char * getMenuText(){
return Constants::MTGBasicAbilities[ability];
}
virtual ostream& toString(ostream& out) const
{
out << "ABasicAbilityModifierUntilEOT ::: mTargets : " << mTargets
<< " ; nbTargets : " << nbTargets
<< " ; modifier : " << modifier
<< " ; stateBeforeActivation : " << stateBeforeActivation
<< " ; ability : " << ability
<< " (";
return TargetAbility::toString(out) << ")";
}
ABasicAbilityModifierUntilEOT * clone() const{
ABasicAbilityModifierUntilEOT * a = NEW ABasicAbilityModifierUntilEOT(*this);
a->isClone = 1;
return a;
}
};
/*Instants that modifies a basic ability until end of turn */
class AInstantBasicAbilityModifierUntilEOT: public InstantAbility{
public:
int stateBeforeActivation;
int ability;
int value;
AInstantBasicAbilityModifierUntilEOT(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _ability, int value):InstantAbility(_id, _source, _target),ability(_ability),value(value){
}
int addToGame(){
MTGCardInstance * _target = (MTGCardInstance *) target;
stateBeforeActivation = _target->basicAbilities[ability];
_target->basicAbilities[ability] = value;
return InstantAbility::addToGame();
}
const char * getMenuText(){
return Constants::MTGBasicAbilities[ability];
}
int destroy(){
MTGCardInstance * _target = (MTGCardInstance *)target;
if (_target) _target->basicAbilities[ability] = stateBeforeActivation;
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "ABasicAbilityModifierUntilEOT ::: stateBeforeActivation : " << stateBeforeActivation
<< " ability : " << ability
<< " (";
return InstantAbility::toString(out) << ")";
}
AInstantBasicAbilityModifierUntilEOT * clone() const{
AInstantBasicAbilityModifierUntilEOT * a = NEW AInstantBasicAbilityModifierUntilEOT(*this);
a->isClone = 1;
return a;
}
};
//Alteration of Ability until of turn (Aura)
class ABasicAbilityAuraModifierUntilEOT: public ActivatedAbility{
public:
AInstantBasicAbilityModifierUntilEOT * ability;
ABasicAbilityAuraModifierUntilEOT(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost, int _ability, int _value = 1):ActivatedAbility(_id,_source, _cost, 0,0){
target = _target;
ability = NEW AInstantBasicAbilityModifierUntilEOT(_id,_source,_target,_ability, _value);
}
int isReactingToClick(MTGCardInstance * card, ManaCost * cost = NULL){
//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 char * getMenuText(){
return ability->getMenuText();
}
ABasicAbilityAuraModifierUntilEOT * clone() const{
ABasicAbilityAuraModifierUntilEOT * a = NEW ABasicAbilityAuraModifierUntilEOT(*this);
a->isClone = 1;
return a;
}
~ABasicAbilityAuraModifierUntilEOT(){
if (!isClone) SAFE_DELETE(ability);
}
};
class AEquip:public TargetAbility{
public:
vector<MTGAbility *> currentAbilities;
AEquip(int _id, MTGCardInstance * _source, ManaCost * _cost=NULL, int doTap=0, int restrictions = ActivatedAbility::AS_SORCERY):TargetAbility(_id,_source,NULL,_cost,restrictions,doTap){
}
int unequip(){
source->target = NULL;
for (size_t i = 0; i < currentAbilities.size(); ++i){
MTGAbility * a = currentAbilities[i];
if(dynamic_cast<AEquip *>(a)){
SAFE_DELETE(a);
continue;
}
GameObserver::GetInstance()->removeObserver(currentAbilities[i]);
}
currentAbilities.clear();
return 1;
}
int equip(MTGCardInstance * equipped){
source->target = equipped;
AbilityFactory af;
af.getAbilities(&currentAbilities,NULL,source);
for (size_t i = 0; i < currentAbilities.size(); ++i){
MTGAbility * a = currentAbilities[i];
if(dynamic_cast<AEquip *>(a)) continue;
a->addToGame();
}
return 1;
}
int resolve(){
MTGCardInstance * mTarget = tc->getNextCardTarget();
if (!mTarget) return 0;
if (mTarget == source) return 0;
unequip();
equip(mTarget);
return 1;
}
const char * getMenuText(){
return "Equip";
}
int testDestroy(){
if (source->target && !game->isInPlay(source->target))
unequip();
return TargetAbility::testDestroy();
}
int destroy(){
unequip();
return TargetAbility::destroy();
}
AEquip * clone() const{
AEquip * a = NEW AEquip(*this);
a->isClone = 1;
return a;
}
};
/*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(int id, MTGCardInstance * _source, CardDescriptor _trigger, ManaCost * _cost, int _life): MTGAbility(id, _source), trigger(_trigger), cost(_cost), life(_life){
}
ASpellCastLife(int id, MTGCardInstance * _source, int color, ManaCost * _cost, int _life): MTGAbility(id, _source), cost(_cost), life(_life){
trigger.setColor(color);
}
int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = 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{
ASpellCastLife * a = NEW ASpellCastLife(*this);
a->isClone = 1;
return a;
}
~ASpellCastLife(){
SAFE_DELETE(cost);
}
};
//Allows to untap at any moment for an amount of mana
class AUnBlocker:public MTGAbility{
public:
ManaCost * cost;
AUnBlocker(int id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost):MTGAbility(id, _source, _target), cost(_cost){
}
int isReactingToClick(MTGCardInstance * _card,ManaCost * mana = NULL){
if (_card == target && game->currentlyActing()->game->inPlay->hasCard(source) && (MTGCardInstance *) _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{
AUnBlocker * a = NEW AUnBlocker(*this);
a->isClone = 1;
return a;
}
};
//Protection From (creature/aura)
class AProtectionFrom: public MTGAbility{
public:
TargetChooser * fromTc;
AProtectionFrom(int id, MTGCardInstance * _source, MTGCardInstance * _target, TargetChooser *fromTc):MTGAbility(id,_source,_target),fromTc(fromTc){
}
int addToGame(){
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->addProtection(fromTc);
return MTGAbility::addToGame();
}
int destroy(){
((MTGCardInstance *)target)->removeProtection(fromTc);
return 1;
}
AProtectionFrom * clone() const{
AProtectionFrom * a = NEW AProtectionFrom(*this);
a->fromTc = fromTc->clone();
a->isClone = 1;
return a;
}
~AProtectionFrom(){
SAFE_DELETE(fromTc);
}
};
//Can't be blocked by...
class ACantBeBlockedBy: public MTGAbility{
public:
TargetChooser * fromTc;
ACantBeBlockedBy(int id, MTGCardInstance * _source, MTGCardInstance * _target, TargetChooser *fromTc):MTGAbility(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();
a->isClone = 1;
return a;
}
~ACantBeBlockedBy(){
SAFE_DELETE(fromTc);
}
};
//Alteration of Power and Toughness (enchantments)
class APowerToughnessModifier: public MTGAbility{
public:
WParsedPT * wppt;
APowerToughnessModifier(int id, MTGCardInstance * _source, MTGCardInstance * _target, WParsedPT * wppt):MTGAbility(id,_source,_target),wppt(wppt){
}
int addToGame(){
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->power += wppt->power.getValue();
_target->addToToughness(wppt->toughness.getValue());
return MTGAbility::addToGame();
}
int destroy(){
((MTGCardInstance *)target)->power -= wppt->power.getValue();
((MTGCardInstance *)target)->addToToughness(- wppt->toughness.getValue());
return 1;
}
APowerToughnessModifier * clone() const{
APowerToughnessModifier * a = NEW APowerToughnessModifier(*this);
a->wppt = NEW WParsedPT(*(a->wppt));
a->isClone = 1;
return a;
}
~APowerToughnessModifier(){
delete(wppt);
}
};
//Alteration of Power and toughness until end of turn (instant)
class AInstantPowerToughnessModifierUntilEOT: public InstantAbility{
public:
WParsedPT * wppt;
AInstantPowerToughnessModifierUntilEOT(int _id, MTGCardInstance * _source, MTGCardInstance * _target, WParsedPT * wppt): InstantAbility(_id, _source, _target), wppt(wppt){
}
int resolve(){
((MTGCardInstance *)target)->power +=wppt->power.getValue();
((MTGCardInstance *)target)->addToToughness(wppt->toughness.getValue());
return 1;
}
int destroy(){
((MTGCardInstance *)target)->power -=wppt->power.getValue();
((MTGCardInstance *)target)->addToToughness(-wppt->toughness.getValue());
return 1;
}
const char * getMenuText(){
sprintf(menuText, "%i/%i",wppt->power.getValue(),wppt->toughness.getValue());
return menuText;
}
AInstantPowerToughnessModifierUntilEOT * clone() const{
AInstantPowerToughnessModifierUntilEOT * a = NEW AInstantPowerToughnessModifierUntilEOT(*this);
a->wppt = NEW WParsedPT(*(a->wppt));
a->isClone = 1;
return a;
}
~AInstantPowerToughnessModifierUntilEOT(){
delete wppt;
}
};
//Alteration of Power and Toughness until end of turn (Aura)
class APowerToughnessModifierUntilEndOfTurn: public ActivatedAbility{
public:
AInstantPowerToughnessModifierUntilEOT * ability;
int counters;
int maxcounters;
APowerToughnessModifierUntilEndOfTurn(int id, MTGCardInstance * _source, MTGCardInstance * _target, WParsedPT * wppt, ManaCost * _cost = NULL, int _maxcounters = 0):ActivatedAbility(id,_source,_cost,0,0),maxcounters(_maxcounters){
counters = 0;
target=_target;
ability = NEW AInstantPowerToughnessModifierUntilEOT(id,_source,_target,wppt);
}
int isReactingToClick(MTGCardInstance * card, ManaCost * cost = NULL){
//The upper level "GenericTargetAbility" takes care of the click so we always return 0 here
return 0;
}
void Update(float dt){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_AFTER_EOT){
counters = 0;
}
ActivatedAbility::Update(dt);
}
int fireAbility(){
return resolve();
}
const char * getMenuText(){
return ability->getMenuText();
}
/* int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
if (!ActivatedAbility::isReactingToClick(card,mana)) return 0;
return (!maxcounters || (counters < maxcounters));
}*/
int resolve(){
MTGAbility * a = ability->clone();
a->target = target;
a->addToGame();
counters++;
return 1;
}
int addToGame(){
resolve();
return ActivatedAbility::addToGame();
}
APowerToughnessModifierUntilEndOfTurn * clone() const{
APowerToughnessModifierUntilEndOfTurn * a = NEW APowerToughnessModifierUntilEndOfTurn(*this);
a->isClone = 1;
return a;
}
~APowerToughnessModifierUntilEndOfTurn(){
if (!isClone) SAFE_DELETE(ability);
}
};
class GenericInstantAbility: public InstantAbility, public NestedAbility{
public:
GenericInstantAbility(int _id, MTGCardInstance * _source, Damageable * _target, MTGAbility * ability): InstantAbility(_id, _source, _target), NestedAbility(ability){
ability->target = _target;
}
int addToGame(){
ability->forceDestroy = -1;
ability->addToGame();
return InstantAbility::addToGame();
}
int destroy(){
ability->forceDestroy = 1;
return InstantAbility::destroy();
}
GenericInstantAbility * clone() const{
GenericInstantAbility * a = NEW GenericInstantAbility(*this);
a->isClone = 1;
return a;
}
};
//Circle of Protections
class ACircleOfProtection: public TargetAbility{
protected:
map<ReplacementEffect*, int> current;
public:
ACircleOfProtection(int _id, MTGCardInstance * source, int _color):TargetAbility(_id,source,NEW SpellOrPermanentTargetChooser(source,_color),NEW ManaCost(),0,0){
cost->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(_target,NULL),
NEW PlayerTargetChooser(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 == Constants::MTG_PHASE_UNTAP) clear();
TargetAbility::Update(dt);
}
~ACircleOfProtection(){
clear();
}
virtual ostream& toString(ostream& out) const
{
out << "ACircleOfProtection ::: (";
return TargetAbility::toString(out) << ")";
}
ACircleOfProtection * clone() const{
ACircleOfProtection * a = NEW ACircleOfProtection(*this);
a->isClone = 1;
return a;
}
};
//Basic regeneration mechanism for a Mana cost
class AStandardRegenerate:public ActivatedAbility{
public:
AStandardRegenerate(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost = NULL):ActivatedAbility(_id,_source,_cost,0,0){
target = _target;
aType = MTGAbility::STANDARD_REGENERATE;
}
int resolve(){
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->regenerate();
return 1;
}
const char * getMenuText(){
return "Regenerate";
}
virtual ostream& toString(ostream& out) const
{
out << "AStandardRegenerate ::: (";
return ActivatedAbility::toString(out) << ")";
}
AStandardRegenerate * clone() const{
AStandardRegenerate * a = NEW AStandardRegenerate(*this);
a->isClone = 1;
return a;
}
};
//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(int id, MTGCardInstance * _source, MTGCardInstance * _target, int _phase, int _life, int _onlyIfTargetTapped=0):MTGAbility(id,_source,_target),life(_life), phase(_phase),onlyIfTargetTapped(_onlyIfTargetTapped){
}
void Update(float dt){
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{
ARegularLifeModifierAura * a = NEW ARegularLifeModifierAura(*this);
a->isClone = 1;
return a;
}
};
//ExaltedAbility (Shards of Alara)
class AExalted:public TriggeredAbility{
public:
int power, toughness;
MTGCardInstance * luckyWinner;
AExalted(int _id, MTGCardInstance * _source, int _power = 1, int _toughness = 1):TriggeredAbility(_id, _source),power(_power),toughness(_toughness){
luckyWinner = NULL;
}
int triggerOnEvent(WEvent * event) {
if (WEventPhaseChange* pe = dynamic_cast<WEventPhaseChange*>(event)) {
if (luckyWinner && Constants::MTG_PHASE_AFTER_EOT == pe->from->id){
luckyWinner->addToToughness(-toughness);
luckyWinner->power-=power;
luckyWinner = NULL;
}
if (Constants::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;
luckyWinner->addToToughness(toughness);
luckyWinner->power+=power;
return 1;
}
AExalted * clone() const{
AExalted * a = NEW AExalted(*this);
a->isClone = 1;
return a;
}
};
//Converts lands to creatures (Kormus bell, Living lands)
class AConvertLandToCreatures:public ListMaintainerAbility{
public:
int type;
int power, toughness;
AConvertLandToCreatures(int _id, MTGCardInstance * _source, const char * _type, int _power = 1, int _toughness = 1):ListMaintainerAbility(_id, _source),power(_power),toughness(_toughness){
type = Subtypes::subtypesList->find(_type);
}
int canBeInList(MTGCardInstance * card){
if (card->hasType(type) && game->isInPlay(card)) return 1;
return 0;
}
int added(MTGCardInstance * card){
card->power = 1;
card->setToughness(1);
card->setSubtype("creature");
return 1;
}
int removed(MTGCardInstance * card){
card->removeType("creature");
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AConvertLandToCreatures ::: power : " << power
<< " ; toughness : " << toughness
<< " ; type : " << type
<< " (";
return ListMaintainerAbility::toString(out) << ")";
}
AConvertLandToCreatures * clone() const{
AConvertLandToCreatures * a = NEW AConvertLandToCreatures(*this);
a->isClone = 1;
return a;
}
};
//Generic Kird Ape
class AAsLongAs:public ListMaintainerAbility, public NestedAbility{
public:
MTGAbility * a;
int includeSelf;
int mini,maxi;
AAsLongAs(int _id, MTGCardInstance * _source, Damageable * _target, TargetChooser * _tc, int _includeSelf, MTGAbility * ability,int mini = 0, int maxi = 0):ListMaintainerAbility(_id, _source,_target),NestedAbility(ability),mini(mini),maxi(maxi){
tc = _tc;
includeSelf = _includeSelf;
tc->targetter = NULL;
ability->source = source;
ability->target = target;
a = NULL;
}
int canBeInList(MTGCardInstance * card){
if ((includeSelf || card!=source) && tc->canTarget(card)) return 1;
return 0;
}
int resolve(){
//TODO check if ability is oneShot ?
updateTargets();
int size = (int) cards.size();
if (maxi && size < maxi && (!mini || size > mini)) addAbilityToGame(); //special case for 0
if (ability->oneShot) a = NULL; //allows to call the effect several times
cards.clear();
players.clear();
return 1;
}
int addAbilityToGame(){
if (a) return 0;
a = ability->clone();
if (a->oneShot){
a->resolve();
delete(a);
}else{
a->addToGame();
}
return 1;
}
int removeAbilityFromGame(){
if (!a) return 0;
game->removeObserver(a);
a = NULL;
return 1;
}
int _added(Damageable * d){
int size = (int) cards.size();
if (maxi && size >= maxi) return removeAbilityFromGame();
if (maxi) return 0;
if (size <= mini) return 0;
return addAbilityToGame();
}
int added(MTGCardInstance * card){
return _added(card);
}
int added(Player * p){
return _added(p);
}
int removed(MTGCardInstance * card){
size_t size = cards.size();
if (mini && (int)size <= mini) return removeAbilityFromGame();
if (maxi && (int)size == maxi-1) return addAbilityToGame();
if (!mini && !maxi && size !=0) return 0;
return removeAbilityFromGame();
}
~AAsLongAs(){
if (!isClone) SAFE_DELETE(ability);
}
AAsLongAs * clone() const{
AAsLongAs * a = NEW AAsLongAs(*this);
a->isClone = 1;
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(int _id, MTGCardInstance * card, TargetChooser * _tc, int _includeSelf, MTGAbility * a):ListMaintainerAbility(_id,card), NestedAbility(a){
tc = _tc;
tc->targetter = NULL;
includeSelf = _includeSelf;
}
int canBeInList(Player *p){
if (tc->canTarget(p)) return 1;
return 0;
}
int canBeInList(MTGCardInstance * card){
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();
delete(a);
}else{
if (d->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE){
a->source = (MTGCardInstance *)d;
}
if (oneShot){
MTGAbility * wrapper = NEW GenericInstantAbility(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()){
game->removeObserver(abilities[card]);
abilities.erase(card);
}
return 1;
}
~ALord(){
if (!isClone) SAFE_DELETE(ability);
}
ALord * clone() const{
ALord * a = NEW ALord(*this);
a->isClone = 1;
return a;
}
};
//Foreach (plague rats...)
class AForeach:public ListMaintainerAbility, public NestedAbility{
public:
int includeSelf;
int mini;
int maxi;
map<Damageable *, MTGAbility *> abilities;
AForeach(int _id, MTGCardInstance * card,Damageable * _target, TargetChooser * _tc, int _includeSelf, MTGAbility * a, int mini = 0, int maxi = 0):ListMaintainerAbility(_id,card,_target), NestedAbility(a),mini(mini),maxi(maxi){
tc = _tc;
tc->targetter = NULL;
includeSelf = _includeSelf;
ability->target = _target;
}
int canBeInList(MTGCardInstance * card){
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();
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;
}
AForeach * clone() const{
AForeach * a = NEW AForeach(*this);
a->isClone = 1;
return a;
}
int resolve(){
//TODO check if ability is oneShot ?
updateTargets();
cards.clear();
players.clear();
return 1;
}
~AForeach(){
if (!isClone) SAFE_DELETE(ability);
}
};
class AThis:public MTGAbility, public NestedAbility{
public:
MTGAbility * a;
ThisDescriptor * td;
AThis(int _id, MTGCardInstance * _source, Damageable * _target, ThisDescriptor * _td, MTGAbility * ability):MTGAbility(_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 dt){
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();
delete(a);
}else{
a->addToGame();
}
return 1;
}
int removeAbilityFromGame(){
if (!a) return 0;
game->removeObserver(a);
a = NULL;
return 1;
}
~AThis(){
if (!isClone) SAFE_DELETE(ability); SAFE_DELETE(td);
}
AThis * clone() const{
AThis * a = NEW AThis(*this);
a->isClone = 1;
return a;
}
};
class AThisForEach:public MTGAbility, public NestedAbility{
public:
ThisDescriptor * td;
vector<MTGAbility *> abilities;
AThisForEach(int _id, MTGCardInstance * _source, Damageable * _target, ThisDescriptor * _td, MTGAbility * ability):MTGAbility(_id, _source,_target),NestedAbility(ability){
td = _td;
ability->source = source;
ability->target = target;
SAFE_DELETE(tc);
}
int removeFromGame(){
return removeAbilityFromGame();
}
int addToGame(){
return MTGAbility::addToGame();
}
void Update(float dt){
resolve();
}
int resolve(){
//TODO check if ability is oneShot ?
int matches;
matches = td->match(source);
if (matches > 0) {
if ((int)(abilities.size()) > matches){
removeAbilityFromGame();
}
for (int i = 0; i < matches - (int)(abilities.size()); i++) {
addAbilityToGame();
}
}
return 1;
}
int addAbilityToGame(){
MTGAbility * a = ability->clone();
a->target = target;
if (a->oneShot){
a->resolve();
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(){
if (!isClone){
SAFE_DELETE(ability);
SAFE_DELETE(td);
}
if (abilities.size()){
removeAbilityFromGame();
}
}
AThisForEach * clone() const{
AThisForEach * a = NEW AThisForEach(*this);
a->isClone = 1;
return a;
}
};
class AADamager:public ActivatedAbilityTP{
public:
WParsedInt * damage;
AADamager(int _id, MTGCardInstance * _source, Targetable * _target, WParsedInt * damage, ManaCost * _cost=NULL, int doTap = 0, int who = TargetChooser::UNSET):ActivatedAbilityTP(_id,_source,_target,_cost,doTap,who),damage(damage){
aType = MTGAbility::DAMAGER;
}
int resolve(){
Damageable * _target = (Damageable *) getTarget();
if(_target){
game->mLayers->stackLayer()->addDamage(source,_target, damage->getValue());
game->mLayers->stackLayer()->resolve();
return 1;
}
return 0;
}
const char * getMenuText(){
return "Damage";
}
AADamager * clone() const{
AADamager * a = NEW AADamager(*this);
a->damage = NEW WParsedInt(*(a->damage));
a->isClone = 1;
return a;
}
~AADamager(){
SAFE_DELETE(damage);
}
};
/* Standard Damager, can choose a NEW target each time the price is paid */
class TADamager:public TargetAbility{
public:
TADamager(int id, MTGCardInstance * card, ManaCost * _cost, WParsedInt * damage, TargetChooser * _tc = NULL, int _tap = 0):TargetAbility(id,card, _tc, _cost,0,_tap){
if (!tc) tc = NEW DamageableTargetChooser(card);
ability = NEW AADamager(id,card,NULL,damage);
}
TADamager * clone() const{
TADamager * a = NEW TADamager(*this);
a->isClone = 1;
return a;
}
};
/* Can tap a target for a cost */
class AATapper:public ActivatedAbility{
public:
AATapper(int id, MTGCardInstance * card, MTGCardInstance * _target,ManaCost * _cost = NULL, int doTap = 0):ActivatedAbility(id,card, _cost,0,doTap){
target = _target;
}
int resolve(){
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target){
while (_target->next) _target=_target->next; //This is for cards such as rampant growth
_target->tap();
}
return 1;
}
const char * getMenuText(){
return "Tap";
}
AATapper * clone() const{
AATapper * a = NEW AATapper(*this);
a->isClone = 1;
return a;
}
};
/* Can untap a target for a cost */
class AAUntapper:public ActivatedAbility{
public:
AAUntapper(int id, MTGCardInstance * card, MTGCardInstance * _target,ManaCost * _cost = NULL, int doTap = 0):ActivatedAbility(id,card, _cost,0,doTap){
target = _target;
}
int resolve(){
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target){
while (_target->next) _target=_target->next; //This is for cards such as rampant growth
_target->untap();
}
return 1;
}
const char * getMenuText(){
return "Untap";
}
AAUntapper * clone() const{
AAUntapper * a = NEW AAUntapper(*this);
a->isClone = 1;
return a;
}
};
// 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(int _id ,MTGCardInstance * card, int _phase, int _condition, int _life = -1, int _controller = 0, MTGGameZone * _zone = NULL):MTGAbility(_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 dt){
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{
ALifeZoneLink * a = NEW ALifeZoneLink(*this);
a->isClone = 1;
return a;
}
};
//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(int _id, MTGCardInstance * _source, const char * _land):MTGAbility(_id, _source){
sprintf(land,"%s",_land);
}
void Update(float dt){
if (source->isAttacker()){
if (!game->opponent()->game->inPlay->hasType(land)){
source->toggleAttacker();
//TODO Improve, there can be race conditions here
}
}
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{
AStrongLandLinkCreature * a = NEW AStrongLandLinkCreature(*this);
a->isClone = 1;
return a;
}
};
//Steal control of a target
class AControlStealAura: public MTGAbility{
public:
Player * originalController;
AControlStealAura(int _id , MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_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{
AControlStealAura * a = NEW AControlStealAura(*this);
a->isClone = 1;
return a;
}
};
//Creatures that kill their blockers
//Ex : Cockatrice
class AOldSchoolDeathtouch:public MTGAbility{
public:
MTGCardInstance * opponents[20];
int nbOpponents;
AOldSchoolDeathtouch(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
nbOpponents = 0;
}
void Update(float dt){
if (newPhase != currentPhase){
if( newPhase == Constants::MTG_PHASE_COMBATDAMAGE){
nbOpponents = 0;
MTGCardInstance * opponent = source->getNextOpponent();
while (opponent && !opponent->hasSubtype("wall")){
opponents[nbOpponents] = opponent;
nbOpponents ++;
opponent = source->getNextOpponent(opponent);
}
}else if (newPhase == Constants::MTG_PHASE_COMBATEND){
for (int i = 0; i < nbOpponents ; i++){
opponents[i]->destroy();
}
}
}
}
int testDestroy(){
if(!game->isInPlay(source) && currentPhase != Constants::MTG_PHASE_UNTAP){
return 0;
}else{
return MTGAbility::testDestroy();
}
}
virtual ostream& toString(ostream& out) const
{
out << "AOldSchoolDeathtouch ::: opponents : " << opponents
<< " ; nbOpponents : " << nbOpponents
<< " (";
return MTGAbility::toString(out) << ")";
}
AOldSchoolDeathtouch * clone() const{
AOldSchoolDeathtouch * a = NEW AOldSchoolDeathtouch(*this);
a->isClone = 1;
return a;
}
};
//Adds types/abilities/P/T to a card (aura)
class ABecomes:public MTGAbility{
public:
list<int>abilities;
list<int>types;
list<int>colors;
WParsedPT * wppt;
ABecomes(int id, MTGCardInstance * source, MTGCardInstance * target, string stypes, WParsedPT * wppt, string sabilities):MTGAbility(id,source,target),wppt(wppt){
//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);
}
}
for (int j = 0; j < Constants::MTG_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){
int id = Subtypes::subtypesList->find(s.substr(0,found));
types.push_back(id);
s = s.substr(found+1);
}else{
int id = Subtypes::subtypesList->find(s);
types.push_back(id);
s = "";
}
}
}
int addToGame(){
MTGCardInstance * _target = (MTGCardInstance *)target;
list<int>::iterator it;
for ( it=types.begin() ; it != types.end(); it++ ){
_target->addType(*it);
}
for ( it=colors.begin() ; it != colors.end(); it++ ){
_target->setColor(*it);
}
for ( it=abilities.begin() ; it != abilities.end(); it++ ){
_target->basicAbilities[*it]++;
}
if (wppt){
_target->power = wppt->power.getValue();
_target->toughness = wppt->toughness.getValue();
_target->life = _target->toughness;
}
return MTGAbility::addToGame();
}
int destroy(){
MTGCardInstance * _target = (MTGCardInstance *)target;
list<int>::iterator it;
for ( it=types.begin() ; it != types.end(); it++ ){
_target->removeType(*it);
}
for ( it=colors.begin() ; it != colors.end(); it++ ){
_target->removeColor(*it);
}
for ( it=abilities.begin() ; it != abilities.end(); it++ ){
_target->basicAbilities[*it]--;
}
return 1;
}
ABecomes * clone() const{
ABecomes * a = NEW ABecomes(*this);
a->wppt = NEW WParsedPT(*(a->wppt));
a->isClone = 1;
return a;
}
~ABecomes(){
delete(wppt);
}
};
//Adds types/abilities/P/T to a card (until end of turn)
class ABecomesUEOT: public InstantAbility{
public:
ABecomes * ability;
ABecomesUEOT(int id, MTGCardInstance * source, MTGCardInstance * target, string types, WParsedPT * wpt, string abilities):InstantAbility(id,source,target){
ability = NEW ABecomes(id,source,target,types,wpt,abilities);
}
int resolve(){
ABecomes * a = ability->clone();
GenericInstantAbility * wrapper = NEW GenericInstantAbility(1,source,(Damageable *)(this->target),a);
wrapper->addToGame();
return 1;
}
const char * getMenuText(){
return ability->getMenuText();
}
ABecomesUEOT * clone() const{
ABecomesUEOT * a = NEW ABecomesUEOT(*this);
a->ability = this->ability->clone();
a->isClone = 1;
return a;
}
~ABecomesUEOT(){
delete ability;
}
};
class APreventAllCombatDamage:public MTGAbility{
public:
string to, from;
REDamagePrevention * re;
APreventAllCombatDamage(int id,MTGCardInstance * source,string to,string from):MTGAbility(id,source),to(to),from(from){
re = NULL;
}
int addToGame(){
if (re) {
OutputDebugString("FATAL:re shouldn't be already set in APreventAllCombatDAMAGE\n");
return 0;
}
TargetChooserFactory tcf;
TargetChooser *toTc = tcf.createTargetChooser(to,source,this);
if (toTc) toTc->targetter = NULL;
TargetChooser *fromTc = tcf.createTargetChooser(from,source,this);
if (fromTc) fromTc->targetter = NULL;
re = NEW REDamagePrevention (this, fromTc, toTc, -1, false, DAMAGE_COMBAT);
game->replacementEffects->add(re);
return MTGAbility::addToGame();
}
int destroy(){
game->replacementEffects->remove(re);
SAFE_DELETE(re);
return 1;
}
APreventAllCombatDamage * clone() const{
APreventAllCombatDamage * a = NEW APreventAllCombatDamage(*this);
a->isClone = 1;
return a;
}
};
//Adds types/abilities/P/T to a card (until end of turn)
class APreventAllCombatDamageUEOT: public InstantAbility{
public:
APreventAllCombatDamage * ability;
vector<APreventAllCombatDamage *> clones;
APreventAllCombatDamageUEOT(int id,MTGCardInstance * source,string to, string from):InstantAbility(id,source){
ability = NEW APreventAllCombatDamage(id,source,to, from);
}
int resolve(){
APreventAllCombatDamage * a = ability->clone();
a->target = this->target;
a->forceDestroy = -1; //Prevent the effect from getting destroyed because its source is not inplay
a->addToGame();
clones.push_back(a);
return 1;
}
int destroy(){
for (size_t i = 0; i < clones.size(); ++i){
clones[i]->forceDestroy = 0;
}
clones.clear();
return 1;
}
const char * getMenuText(){
return ability->getMenuText();
}
APreventAllCombatDamageUEOT * clone() const{
APreventAllCombatDamageUEOT * a = NEW APreventAllCombatDamageUEOT(*this);
a->ability = this->ability->clone();
a->isClone = 1;
return a;
}
~APreventAllCombatDamageUEOT(){
delete ability;
}
};
//Upkeep Cost
class AUpkeep:public ActivatedAbility, public NestedAbility{
public:
int paidThisTurn;
int phase;
int once;
AUpkeep(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int _tap = 0, int restrictions = 0, int _phase = Constants::MTG_PHASE_UPKEEP, int _once = 0):ActivatedAbility(_id, card,_cost,restrictions,_tap),NestedAbility(a),phase(_phase),once(_once){
paidThisTurn = 0;
}
void Update(float dt){
// once: 0 means always go off, 1 means go off only once, 2 means go off only once and already has.
if (newPhase != currentPhase && source->controller() == game->currentPlayer && once < 2){
if (newPhase == Constants::MTG_PHASE_UNTAP){
paidThisTurn = 0;
}else if( newPhase == phase + 1 && !paidThisTurn){
ability->resolve();
}
if(newPhase == phase + 1 && once) once = 2;
}
ActivatedAbility::Update(dt);
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
if (currentPhase != phase || paidThisTurn || once >= 2) return 0;
return ActivatedAbility::isReactingToClick(card,mana);
}
int resolve(){
paidThisTurn = 1;
return 1;
}
const char * getMenuText(){
return "Upkeep";
}
virtual ostream& toString(ostream& out) const
{
out << "AUpkeep ::: paidThisTurn : " << paidThisTurn
<< " (";
return ActivatedAbility::toString(out) << ")";
}
~AUpkeep(){
if(!isClone) SAFE_DELETE(ability);
}
AUpkeep * clone() const{
AUpkeep * a = NEW AUpkeep(*this);
a->isClone = 1;
return a;
}
};
/*
Specific Classes
*/
// 1092 Specific to Aladdin's Lamp
class AAladdinsLamp: public TargetAbility{
public:
CardDisplay cd;
int nbcards;
int init;
AAladdinsLamp(int id, MTGCardInstance * card) : TargetAbility(id,card) {
cost = NEW ManaCost();
cost->x();
cd = CardDisplay(1, game, SCREEN_WIDTH/2, SCREEN_HEIGHT/2, NULL);
int zones[] = {MTGGameZone::MY_LIBRARY};
tc = NEW TargetZoneChooser(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(cost);
nbcards = 0;
MTGGameZone * library = game->currentlyActing()->game->library;
while (nbcards < wished && nbcards < library->nb_cards){
cd.AddCard(library->cards[library->nb_cards - 1 - nbcards]);
nbcards++;
}
init = 1;
}
cd.Update(dt);
// cd.CheckUserInput(dt);
}
}
void Render(float dt){
if (waitingForAnswer){
cd.Render();
}
}
int fireAbility(){
source->tap();
MTGLibrary * library = game->currentlyActing()->game->library;
MTGCardInstance * card = library->removeCard(tc->getNextCardTarget());
library->shuffleTopToBottom(nbcards - 1 );
library->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{
AAladdinsLamp * a = NEW AAladdinsLamp(*this);
a->isClone = 1;
return a;
}
};
// Armageddon Clock
class AArmageddonClock:public MTGAbility{
public:
int counters;
ManaCost cost;
AArmageddonClock(int id, MTGCardInstance * _source):MTGAbility(id, _source){
counters = 0;
int _cost[] = {Constants::MTG_COLOR_ARTIFACT, 4};
cost = ManaCost(_cost,1);
}
void Update(float dt){
if (newPhase != currentPhase){
if (newPhase == Constants::MTG_PHASE_UPKEEP && game->currentPlayer->game->inPlay->hasCard(source)){
counters ++;
}else if (newPhase == Constants::MTG_PHASE_DRAW && counters > 0 && game->currentPlayer->game->inPlay->hasCard(source)){ //End of upkeep = beginning of draw
GameObserver::GetInstance()->mLayers->stackLayer()->addDamage(source,GameObserver::GetInstance()->players[0], counters);
GameObserver::GetInstance()->mLayers->stackLayer()->addDamage(source,GameObserver::GetInstance()->players[1], counters);
}
}
}
int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL){
if (counters > 0 && _card == source && currentPhase == Constants::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{
AArmageddonClock * a = NEW AArmageddonClock(*this);
a->isClone = 1;
return a;
}
};
// Clockwork Beast
class AClockworkBeast:public MTGAbility{
public:
int counters;
ManaCost cost;
AClockworkBeast(int id, MTGCardInstance * _source):MTGAbility(id, _source){
counters = 7;
((MTGCardInstance *)target)->power+=7;
int _cost[] = {Constants::MTG_COLOR_ARTIFACT, 1};
cost = ManaCost(_cost,1);
}
void Update(float dt){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_COMBATEND){
if (((MTGCardInstance *)source)->isAttacker() || ((MTGCardInstance *)source)->isDefenser()){
counters--;
((MTGCardInstance *)target)->power-=1;
}
}
}
int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL){
if (counters < 7 && _card == source && currentPhase == Constants::MTG_PHASE_UPKEEP && game->currentPlayer->game->inPlay->hasCard(source)){
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 ++;
((MTGCardInstance *)target)->power++;
((MTGCardInstance *)target)->tap();
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AClockworkBeast ::: counters : " << counters
<< " ; cost : " << cost
<< " (";
return MTGAbility::toString(out) << ")";
}
AClockworkBeast * clone() const{
AClockworkBeast * a = NEW AClockworkBeast(*this);
a->isClone = 1;
return a;
}
};
//1102: Conservator
class AConservator: public MTGAbility{
public:
int canprevent;
ManaCost cost;
AConservator(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
canprevent = 0;
int _cost[] = {Constants::MTG_COLOR_ARTIFACT, 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->mCount-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->mCount-1;j >=0; j--){
alterDamage(((Damage *)damages->mObjects[j]));
}
}
}
}
return 1;
}
void Update(float dt){
alterDamage();
}
int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = 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{
AConservator * a = NEW AConservator(*this);
a->isClone = 1;
return a;
}
};
//1345 Farmstead
class AFarmstead:public ActivatedAbility{
public:
int usedThisTurn;
AFarmstead(int _id, MTGCardInstance * source, MTGCardInstance * _target):ActivatedAbility(_id, source,0,1,0){
int _cost[] = {Constants::MTG_COLOR_WHITE, 2};
cost = NEW ManaCost(_cost,1);
target = _target;
usedThisTurn = 0;
}
void Update(float dt){
if (newPhase != currentPhase && newPhase != Constants::MTG_PHASE_UPKEEP){
usedThisTurn = 0;
}
ActivatedAbility::Update(dt);
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
if (!ActivatedAbility::isReactingToClick(card,mana)) return 0;
if (currentPhase != Constants::MTG_PHASE_UPKEEP) return 0;
if (usedThisTurn) return 0;
return 1;
}
int resolve(){
source->controller()->life++;
usedThisTurn = 1;
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AFarmstead ::: usedThisTurn : " << usedThisTurn
<< " (";
return ActivatedAbility::toString(out) << ")";
}
AFarmstead * clone() const{
AFarmstead * a = NEW AFarmstead(*this);
a->isClone = 1;
return a;
}
};
//1112 Howling Mine
class AHowlingMine:public MTGAbility{
public:
AHowlingMine(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){}
void Update(float dt){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_DRAW && !source->isTapped()){
game->mLayers->stackLayer()->addDraw(game->currentPlayer);
}
}
virtual ostream& toString(ostream& out) const
{
out << "AHowlingMine ::: (";
return MTGAbility::toString(out) << ")";
}
AHowlingMine * clone() const{
AHowlingMine * a = NEW AHowlingMine(*this);
a->isClone = 1;
return a;
}
};
//Kjeldoran Frostbeast
class AKjeldoranFrostbeast:public MTGAbility{
public:
MTGCardInstance * opponents[20];
int nbOpponents;
AKjeldoranFrostbeast(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
nbOpponents = 0;
}
void Update(float dt){
if (newPhase != currentPhase){
if( newPhase == Constants::MTG_PHASE_COMBATEND){
nbOpponents = 0;
MTGCardInstance * opponent = source->getNextOpponent();
while (opponent && !opponent->hasSubtype("wall")){
opponents[nbOpponents] = opponent;
nbOpponents ++;
opponent = source->getNextOpponent(opponent);
}
if (source->isInPlay()) {
for (int i = 0; i < nbOpponents ; i++){
opponents[i]->destroy();
}
}
}
}
}
int testDestroy(){
if(!game->isInPlay(source) && currentPhase != Constants::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{
AKjeldoranFrostbeast * a = NEW AKjeldoranFrostbeast(*this);
a->isClone = 1;
return a;
}
};
//Living Artifact
class ALivingArtifact:public MTGAbility{
public:
int usedThisTurn;
int counters;
Damage * latest;
ALivingArtifact(int _id, MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_id,_source,_target){
usedThisTurn = 0;
counters = 0;
latest = NULL;
}
int receiveEvent(WEvent * event){
WEventDamage * e = dynamic_cast<WEventDamage *>(event);
if (!e) return 0;
Player * p = dynamic_cast<Player *>(e->damage->target);
if (!p) return 0;
if (p != source->controller()) return 0;
counters+=e->damage->damage;
return 1; //is this meant to return 0 or 1?
}
int isReactingtoclick(MTGCardInstance * card, ManaCost * mana = NULL){
if (currentPhase == Constants::MTG_PHASE_UPKEEP && card == source && game->currentPlayer == source->controller() && counters && !usedThisTurn){
return 1;
}
return 0;
}
int reactToClick(MTGCardInstance * card){
source->controller()->life+=1;
counters--;
usedThisTurn = 1;
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "ALivingArtifact ::: usedThisTurn : " << usedThisTurn
<< " ; counters : " << counters
<< " ; latest : " << latest
<< " (";
return MTGAbility::toString(out) << ")";
}
ALivingArtifact * clone() const{
ALivingArtifact * a = NEW ALivingArtifact(*this);
a->isClone = 1;
return a;
}
};
//1143 Animate Dead
class AAnimateDead:public MTGAbility{
public:
AAnimateDead(int _id, MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_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(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{
AAnimateDead * a = NEW AAnimateDead(*this);
a->isClone = 1;
return a;
}
};
//1159 Erg Raiders
class AErgRaiders:public MTGAbility{
public:
int attackedThisTurn;
AErgRaiders(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
attackedThisTurn = 1;
}
void Update(float dt){
if (newPhase != currentPhase){
Player * controller = source->controller();
if (newPhase == Constants::MTG_PHASE_COMBATDAMAGE && game->currentPlayer == controller){
if (source->isAttacker()){
attackedThisTurn = 1;
}
}else if (newPhase == Constants::MTG_PHASE_UNTAP){
if (game->currentPlayer != controller && !attackedThisTurn){
game->mLayers->stackLayer()->addDamage(source, controller,2);
}else if (game->currentPlayer == controller) {
attackedThisTurn = 0;
}
}
}
}
AErgRaiders * clone() const{
AErgRaiders * a = NEW AErgRaiders(*this);
a->isClone = 1;
return a;
}
};
//Fastbond
class AFastbond:public TriggeredAbility{
public:
int alreadyPlayedALand;
int previous;
AFastbond(int _id, MTGCardInstance * card):TriggeredAbility(_id, card){
alreadyPlayedALand = 0;
if (source->controller()->canPutLandsIntoPlay == 0){
alreadyPlayedALand = 1;
source->controller()->canPutLandsIntoPlay = 1;
}
previous = source->controller()->canPutLandsIntoPlay;
}
void Update(float dt){
if (newPhase!=currentPhase && newPhase == Constants::MTG_PHASE_UNTAP){
alreadyPlayedALand = 0;
}
TriggeredAbility::Update(dt);
}
int trigger(){
if(source->controller()->canPutLandsIntoPlay==0 && previous == 1){
previous = 0;
source->controller()->canPutLandsIntoPlay = 1;
if (alreadyPlayedALand) return 1;
alreadyPlayedALand = 1;
return 0;
}
previous = source->controller()->canPutLandsIntoPlay;
return 0;
}
int resolve(){
game->mLayers->stackLayer()->addDamage(source, source->controller(), 1);
game->mLayers->stackLayer()->resolve();
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AFastbond ::: alreadyPlayedALand : " << alreadyPlayedALand
<< " ; previous : " << previous
<< " (";
return TriggeredAbility::toString(out) << ")";
}
AFastbond * clone() const{
AFastbond * a = NEW AFastbond(*this);
a->isClone = 1;
return a;
}
};
//1165 Hypnotic Specter
class AHypnoticSpecter:public MTGAbility{
public:
AHypnoticSpecter(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
}
int receiveEvent(WEvent * event){
WEventDamage * e = dynamic_cast<WEventDamage *>(event);
if (!e) return 0;
if (e->damage->source != source) return 0;
Player * p = dynamic_cast<Player *>(e->damage->target);
if (!p) return 0;
p->game->discardRandom(p->game->hand);
return 1; //is this meant to return 0 or 1?
}
AHypnoticSpecter * clone() const{
AHypnoticSpecter * a = NEW AHypnoticSpecter(*this);
a->isClone = 1;
return a;
}
};
//1117 Jandor's Ring
class AJandorsRing:public ActivatedAbility{
public:
AJandorsRing(int _id, MTGCardInstance * _source):ActivatedAbility(_id,_source, NEW ManaCost()){
cost->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{
AJandorsRing * a = NEW AJandorsRing(*this);
a->isClone = 1;
return a;
}
};
//Kudzu.
//What happens when there are no targets ???
class AKudzu: public TargetAbility{
public:
AKudzu(int _id, MTGCardInstance * card, MTGCardInstance * _target):TargetAbility(_id,card, NEW TypeTargetChooser("land",card)){
tc->toggleTarget(_target);
target = _target;
}
int receiveEvent(WEvent * event){
if (WEventCardTap* wect = dynamic_cast<WEventCardTap*>(event)) {
if (wect->before == false && wect->after == true && wect->card == target){
MTGCardInstance * _target = (MTGCardInstance *)target;
if (!_target->isInPlay()) return 0;
target = _target->controller()->game->putInGraveyard(_target);
reactToClick(source);
return 1;
}
}
return 0;
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
MTGCardInstance * _target = (MTGCardInstance *)target;
if (card == source && (!_target || !_target->isInPlay())){
#if defined (WIN32) || defined (LINUX)
OutputDebugString("Kudzu Reacts to click !\n");
#endif
return 1;
}
return 0;
}
int resolve(){
target = tc->getNextCardTarget();
source->target = (MTGCardInstance *) target;
return 1;
}
int testDestroy(){
int stillLandsInPlay = 0;
for (int i = 0; i < 2; i++){
if (game->players[i]->game->inPlay->hasType("Land")) stillLandsInPlay = 1;
}
if (!stillLandsInPlay){
source->controller()->game->putInGraveyard(source);
return 1;
}
if (!game->isInPlay(source)){
return 1;
}
return 0;
}
AKudzu * clone() const{
AKudzu * a = NEW AKudzu(*this);
a->isClone = 1;
return a;
}
};
//1172 Pestilence
class APestilence: public ActivatedAbility{
public:
APestilence(int _id, MTGCardInstance * card):ActivatedAbility(_id, card, NEW ManaCost(), 0,0){
cost->add(Constants::MTG_COLOR_BLACK, 1);
}
void Update(float dt){
if (newPhase !=currentPhase && newPhase == Constants::MTG_PHASE_EOT){
if (!game->players[0]->game->inPlay->hasType("creature") && !game->players[1]->game->inPlay->hasType("creature")){
source->controller()->game->putInGraveyard(source);
}
}
}
int resolve(){
for (int i = 0; i < 2 ; i++){
MTGInPlay * inplay = game->players[i]->game->inPlay;
for (int j = inplay->nb_cards - 1 ; j >=0; j--){
if (inplay->cards[j]->isCreature()) game->mLayers->stackLayer()->addDamage(source,inplay->cards[j],1);
}
game->mLayers->stackLayer()->addDamage(source,game->players[i],1);
}
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "APestilence ::: (";
return ActivatedAbility::toString(out) << ")";
}
APestilence * clone() const{
APestilence * a = NEW APestilence(*this);
a->isClone = 1;
return a;
}
};
//Power Leak
class APowerLeak:public TriggeredAbility{
public:
int damagesToDealThisTurn;
ManaCost cost;
APowerLeak(int _id, MTGCardInstance * _source, MTGCardInstance * _target):TriggeredAbility(_id, _source, _target){
cost.add(Constants::MTG_COLOR_ARTIFACT, 1);
damagesToDealThisTurn = 0;
}
void Update(float dt){
MTGCardInstance * _target = (MTGCardInstance *) target;
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UPKEEP && _target->controller() == game->currentPlayer){
damagesToDealThisTurn = 2;
}
TriggeredAbility::Update(dt);
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
MTGCardInstance * _target = (MTGCardInstance *) target;
if (damagesToDealThisTurn && currentPhase == Constants::MTG_PHASE_UPKEEP && card==source && _target->controller() == game->currentPlayer){
if (game->currentPlayer->getManaPool()->canAfford(& cost)) return 1;
}
return 0;
}
int reactToclick(MTGCardInstance * card){
game->currentPlayer->getManaPool()->pay( & cost);
damagesToDealThisTurn--;
return 1;
}
int trigger(){
MTGCardInstance * _target = (MTGCardInstance *) target;
if (newPhase != currentPhase && newPhase == Constants::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{
APowerLeak * a = NEW APowerLeak(*this);
a->isClone = 1;
return a;
}
};
//1176 Sacrifice
class ASacrifice:public InstantAbility{
public:
ASacrifice(int _id, MTGCardInstance * _source, MTGCardInstance * _target):InstantAbility(_id, _source){
target = _target;
}
int resolve(){
MTGCardInstance * _target = (MTGCardInstance *) target;
if (_target->isInPlay()){
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{
ASacrifice * a = NEW ASacrifice(*this);
a->isClone = 1;
return a;
}
};
//1178 Scavenging Ghoul
class AScavengingGhoul:public MTGAbility{
public:
int counters;
AScavengingGhoul(int _id, MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_id, _source, _target){
counters = 0;
}
void Update(float dt){
//TODO
}
int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL){
if (counters > 0 && _card == source && game->currentlyActing()->game->inPlay->hasCard(source)){
return 1;
}
return 0;
}
int reactToClick(MTGCardInstance * _card){
if (!isReactingToClick( _card)) return 0;
counters--;
source->regenerate();
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AScavengingGhoul ::: counters : " << counters
<< " (";
return MTGAbility::toString(out) << ")";
}
AScavengingGhoul * clone() const{
AScavengingGhoul * a = NEW AScavengingGhoul(*this);
a->isClone = 1;
return a;
}
};
//1235 Aspect of Wolf
class AAspectOfWolf:public ListMaintainerAbility{
public:
int color;
AAspectOfWolf(int _id, MTGCardInstance * _source, MTGCardInstance * _target):ListMaintainerAbility(_id, _source, _target){
}
int canBeInList(MTGCardInstance * card){
if (card->controller() == source->controller() && card->hasType("forest") && game->isInPlay(card)) return 1;
return 0;
}
int added(MTGCardInstance * card){
MTGCardInstance * _target = (MTGCardInstance *) target;
int size = cards.size();
if (size % 2 == 0){
_target->power += 1;
}else{
_target->addToToughness(1);
}
return 1;
}
int removed(MTGCardInstance * card){
MTGCardInstance * _target = (MTGCardInstance *) target;
int size = cards.size();
if (size % 2 == 1){
_target->power -= 1;
}else{
_target->addToToughness(-1);
}
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AAspectOfWolf ::: color : " << color
<< " (";
return ListMaintainerAbility::toString(out) << ")";
}
AAspectOfWolf * clone() const{
AAspectOfWolf * a = NEW AAspectOfWolf(*this);
a->isClone = 1;
return a;
}
};
//1284 Dragon Whelp
class ADragonWhelp: public APowerToughnessModifierUntilEndOfTurn{
public:
ADragonWhelp(int id, MTGCardInstance * card):APowerToughnessModifierUntilEndOfTurn(id, card, card, NEW WParsedPT(1, 0), NEW ManaCost()){
cost->add(Constants::MTG_COLOR_RED, 1);
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
if (!ActivatedAbility::isReactingToClick(card,mana)) return 0;
return (!maxcounters || (counters < maxcounters));
}
void Update(float dt){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_AFTER_EOT && counters > 3){
source->controller()->game->putInGraveyard(source);
}
APowerToughnessModifierUntilEndOfTurn::Update(dt);
}
virtual ostream& toString(ostream& out) const
{
out << "ADragonWhelp ::: (";
return APowerToughnessModifierUntilEndOfTurn::toString(out) << ")";
}
ADragonWhelp * clone() const{
ADragonWhelp * a = NEW ADragonWhelp(*this);
a->isClone = 1;
return a;
}
};
//1288 EarthBind
class AEarthbind:public ABasicAbilityModifier{
public:
AEarthbind(int _id, MTGCardInstance * _source, MTGCardInstance * _target):ABasicAbilityModifier(_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{
AEarthbind * a = NEW AEarthbind(*this);
a->isClone = 1;
return a;
}
};
//1291 Fireball
class AFireball:public InstantAbility{
public:
AFireball(int _id, MTGCardInstance * card, Spell * spell, int x):InstantAbility(_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{
AFireball * a = NEW AFireball(*this);
a->isClone = 1;
return a;
}
};
//1351 Island Sanctuary
class AIslandSanctuary:public MTGAbility{
public:
int initThisTurn;
AIslandSanctuary(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
initThisTurn = 0;
}
void Update(float dt){
if (currentPhase == Constants::MTG_PHASE_UNTAP && game->currentPlayer == source->controller()) initThisTurn = 0;
if (initThisTurn && currentPhase == Constants::MTG_PHASE_COMBATATTACKERS && 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->isAttacker() && !card->basicAbilities[Constants::FLYING] && !card->basicAbilities[Constants::ISLANDWALK]) source->toggleAttacker();
}
}
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
if (card==source && game->currentPlayer == card->controller() && currentPhase == Constants::MTG_PHASE_DRAW){
Interruptible * action = game->mLayers->stackLayer()->getAt(-1);
if (action->type == ACTION_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{
AIslandSanctuary * a = NEW AIslandSanctuary(*this);
a->isClone = 1;
return a;
}
};
//Stasis
class AStasis:public ActivatedAbility{
public:
int paidThisTurn;
AStasis(int _id, MTGCardInstance * card):ActivatedAbility(_id,card, NEW ManaCost(),1,0){
paidThisTurn = 1;
cost->add(Constants::MTG_COLOR_BLUE,1);
}
void Update(float dt){
//Upkeep Cost
if (newPhase !=currentPhase){
if (newPhase == Constants::MTG_PHASE_UPKEEP){
paidThisTurn = 0;
}else if (!paidThisTurn && newPhase > Constants::MTG_PHASE_UPKEEP && game->currentPlayer==source->controller() ){
game->currentPlayer->game->putInGraveyard(source);
paidThisTurn = 1;
}
}
//Stasis Effect
for (int i = 0; i < 2; i++){
game->phaseRing->removePhase(Constants::MTG_PHASE_UNTAP,game->players[i]);
}
//Parent Class Method Call
ActivatedAbility::Update(dt);
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
return (!paidThisTurn && currentPhase == Constants::MTG_PHASE_UPKEEP && ActivatedAbility::isReactingToClick(card,mana));
}
int resolve(){
paidThisTurn = 1;
return 1;
}
int destroy(){
for (int i = 0; i < 2; i++){
game->phaseRing->addPhaseBefore(Constants::MTG_PHASE_UNTAP,game->players[i],Constants::MTG_PHASE_UPKEEP,game->players[i]);
}
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AStasis ::: paidThisTurn : " << paidThisTurn
<< " (";
return ActivatedAbility::toString(out) << ")";
}
AStasis * clone() const{
AStasis * a = NEW AStasis(*this);
a->isClone = 1;
return a;
}
};
//--------------Addon Abra------------------
//Basilik --> needs to be made more generic to avoid duplicate (also something like if opponent=type then ...)
class ABasilik:public MTGAbility{
public:
MTGCardInstance * opponents[20];
int nbOpponents;
ABasilik (int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
nbOpponents = 0;
}
void Update(float dt){
if (newPhase != currentPhase){
if( newPhase == Constants::MTG_PHASE_COMBATDAMAGE){
nbOpponents = 0;
MTGCardInstance * opponent = source->getNextOpponent();
while (opponent){
opponents[nbOpponents] = opponent;
nbOpponents ++;
opponent = source->getNextOpponent(opponent);
}
}else if (newPhase == Constants::MTG_PHASE_COMBATEND){
for (int i = 0; i < nbOpponents ; i++){
game->mLayers->stackLayer()->addPutInGraveyard(opponents[i]);
}
}
}
}
virtual ostream& toString(ostream& out) const
{
out << "ABasilik ::: opponents : " << opponents
<< " ; nbOpponents : " << nbOpponents
<< " (";
return MTGAbility::toString(out) << ")";
}
ABasilik * clone() const{
ABasilik * a = NEW ABasilik(*this);
a->isClone = 1;
return a;
}
};
//Lavaborn - quick and very dirty ;) copy of ALifezonelink but without the multiplier.
class ALavaborn:public MTGAbility{
public:
int phase;
int condition;
int life;
int controller;
int nbcards;
MTGGameZone * zone;
ALavaborn(int _id ,MTGCardInstance * card, int _phase, int _condition, int _life, int _controller = 0, MTGGameZone * _zone = NULL):MTGAbility(_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 dt){
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;
}else{
game->mLayers->stackLayer()->addDamage(source,game->currentPlayer,-life);
}
}
}
}
}
virtual ostream& toString(ostream& out) const
{
out << "ALavaborn ::: phase : " << phase
<< " ; condition : " << condition
<< " ; life : " << life
<< " ; controller : " << controller
<< " ; nbcards : " << nbcards
<< " (";
return MTGAbility::toString(out) << ")";
}
ALavaborn * clone() const{
ALavaborn * a = NEW ALavaborn(*this);
a->isClone = 1;
return a;
}
};
//Generic Millstone
class AADepleter:public ActivatedAbilityTP{
public:
int nbcards;
AADepleter(int _id, MTGCardInstance * card, Targetable * _target, int nbcards = 1, ManaCost * _cost=NULL, int _tap = 0, int who = TargetChooser::UNSET):ActivatedAbilityTP(_id,card, _target,_cost,_tap,who),nbcards(nbcards){
}
int resolve(){
Targetable * _target = getTarget();
Player * player;
if (_target){
if (_target->typeAsTarget() == TARGET_CARD){
player = ((MTGCardInstance *)_target)->controller();
}else{
player = (Player *) _target;
}
MTGLibrary * library = player->game->library;
for (int i = 0; i < nbcards; i++){
if (library->nb_cards)
player->game->putInZone(library->cards[library->nb_cards-1],library, player->game->graveyard);
}
}
return 1;
}
const char * getMenuText(){
return "Deplete";
}
AADepleter * clone() const{
AADepleter * a = NEW AADepleter(*this);
a->isClone = 1;
return a;
}
};
//Shuffle
class AAShuffle:public ActivatedAbilityTP{
public:
AAShuffle(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost=NULL, int _tap = 0, int who = TargetChooser::UNSET):ActivatedAbilityTP(_id,card, _target,_cost,_tap,who){
}
int resolve(){
Targetable * _target = getTarget();
Player * player;
if (_target){
if (_target->typeAsTarget() == TARGET_CARD){
player = ((MTGCardInstance *)_target)->controller();
}else{
player = (Player *) _target;
}
MTGLibrary * library = player->game->library;
library->shuffle();
}
return 1;
}
const char * getMenuText(){
return "Shuffle";
}
AAShuffle * clone() const{
AAShuffle * a = NEW AAShuffle(*this);
a->isClone = 1;
return a;
}
};
//Random Discard
class AARandomDiscarder:public ActivatedAbilityTP{
public:
int nbcards;
AARandomDiscarder(int _id, MTGCardInstance * card, Targetable * _target, int nbcards = 1, ManaCost * _cost=NULL, int _tap = 0,int who=TargetChooser::UNSET):ActivatedAbilityTP(_id,card, _target,_cost,_tap,who),nbcards(nbcards){
}
int resolve(){
Targetable * _target = getTarget();
Player * player;
if (_target){
if (_target->typeAsTarget() == TARGET_CARD){
player = ((MTGCardInstance *)_target)->controller();
}else{
player = (Player *) _target;
}
for (int i = 0; i < nbcards; i++){
player->game->discardRandom(player->game->hand);
}
}
return 1;
}
const char * getMenuText(){
return "Discard Random";
}
AARandomDiscarder * clone() const{
AARandomDiscarder * a = NEW AARandomDiscarder(*this);
a->isClone = 1;
return a;
}
};
//Minion of Leshrac
class AMinionofLeshrac: public TargetAbility{
public:
int paidThisTurn;
AMinionofLeshrac(int _id, MTGCardInstance * source):TargetAbility(_id, source, NEW CreatureTargetChooser(),0,1,0){
paidThisTurn = 1;
}
void Update(float dt){
if (newPhase != currentPhase && source->controller() == game->currentPlayer){
if (newPhase == Constants::MTG_PHASE_UNTAP){
paidThisTurn = 0;
}else if( newPhase == Constants::MTG_PHASE_UPKEEP + 1 && !paidThisTurn){
game->mLayers->stackLayer()->addDamage(source,source->controller(), 5);
source->tap();
}
}
TargetAbility::Update(dt);
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
if (currentPhase != Constants::MTG_PHASE_UPKEEP || paidThisTurn) return 0;
return TargetAbility::isReactingToClick(card,mana);
}
int resolve(){
MTGCardInstance * card = tc->getNextCardTarget();
if (card && card != source && card->controller() == source->controller()){
card->controller()->game->putInGraveyard(card);
paidThisTurn = 1;
return 1;
}
return 0;
}
virtual ostream& toString(ostream& out) const
{
out << "AMinionofLeshrac ::: paidThisTurn : " << paidThisTurn
<< " (";
return TargetAbility::toString(out) << ")";
}
AMinionofLeshrac * clone() const{
AMinionofLeshrac * a = NEW AMinionofLeshrac(*this);
a->isClone = 1;
return a;
}
};
//Rampage ability
class ARampageAbility:public MTGAbility{
public:
int nbOpponents;
int PowerModifier;
int ToughnessModifier;
int MaxOpponent;
ARampageAbility(int _id, MTGCardInstance * _source,int _PowerModifier, int _ToughnessModifier, int _MaxOpponent):MTGAbility(_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;
source->power += PowerModifier * (nbOpponents - MaxOpponent);
source->addToToughness(ToughnessModifier * (nbOpponents - MaxOpponent));
}
else if (WEventPhaseChange* pe = dynamic_cast<WEventPhaseChange*>(event)) {
if (Constants::MTG_PHASE_AFTER_EOT == pe->to->id && nbOpponents > MaxOpponent)
{
source->power -= PowerModifier * (nbOpponents - MaxOpponent);
source->addToToughness(-ToughnessModifier * (nbOpponents - MaxOpponent));
nbOpponents = 0;
}
}
return 1;
}
ARampageAbility * clone() const{
ARampageAbility * a = NEW ARampageAbility(*this);
a->isClone = 1;
return a;
}
};
//Instant Steal control of a target
class AInstantControlSteal: public InstantAbility{
public:
Player * TrueController;
Player * TheftController;
AInstantControlSteal(int _id , MTGCardInstance * _source, MTGCardInstance * _target):InstantAbility(_id, _source, _target){
TrueController = _target->controller();
TheftController = source->controller();
MTGCardInstance * copy = _target->changeController(TheftController);
target = copy;
source->target = copy;
copy->summoningSickness = 0;
}
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{
AInstantControlSteal * a = NEW AInstantControlSteal(*this);
a->isClone = 1;
return a;
}
};
//Angelic Chorus (10E)
class AAngelicChorus: public ListMaintainerAbility{
public:
int init;
AAngelicChorus(int id, MTGCardInstance * _source):ListMaintainerAbility(id, _source){
init = 0;
}
void Update(float dt){
ListMaintainerAbility::Update(dt);
init = 1;
}
int canBeInList(MTGCardInstance * card){
if (card->hasType(Subtypes::TYPE_CREATURE) && game->isInPlay(card)) return 1;
return 0;
}
int added(MTGCardInstance * card){
if (!init) return 0;
if (source->controller() == game->currentlyActing()){
card->controller()->life+= card->toughness;
}
return 1;
}
int removed(MTGCardInstance * card){
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AAngelicChorus ::: init : " << init
<< " (";
return ListMaintainerAbility::toString(out) << ")";
}
AAngelicChorus * clone() const{
AAngelicChorus * a = NEW AAngelicChorus(*this);
a->isClone = 1;
return a;
}
};
#endif