Files
wagic/projects/mtg/include/AllAbilities.h
T
wagic.the.homebrew@gmail.com 76d386be06 Erwan
- fixed a bug with bottle gnomes (would give life to opponent even if you steal control)
- huge update for abilities life,draw,damage,deplete,discard in the parser. This breaks some cards (so we need to fix them) but allows more flexibility in the future. See my post on the forum
2009-07-25 14:57:27 +00:00

4338 lines
118 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 <JGui.h>
#include <hge/hgeparticle.h>
#include <map>
using std::map;
//
//Triggers
//
class TrCardAddedToZone:public TriggeredAbility{
public:
TargetChooser * toTc;
TargetZoneChooser * fromTc;
TrCardAddedToZone(int id,MTGCardInstance * source, TargetChooser * toTc, TargetZoneChooser * fromTc = NULL):TriggeredAbility(id,source), fromTc(fromTc),toTc(toTc){
}
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 (fromTc && !fromTc->targetsZone(e->from)) return 0;
return 1;
}
~TrCardAddedToZone(){
SAFE_DELETE(toTc);
SAFE_DELETE(fromTc);
}
TrCardAddedToZone * clone() const{
TrCardAddedToZone * a = NEW TrCardAddedToZone(*this);
a->isClone = 1;
return a;
}
};
//counters
class AACounter: public ActivatedAbility{
public:
int nb;
int power;
int toughness;
AACounter(int id, MTGCardInstance * _source, MTGCardInstance * _target, int _power, int _toughness, int nb,ManaCost * cost=NULL, int doTap = 0):ActivatedAbility(id,_source,cost,0,doTap),power(_power),toughness(_toughness),nb(nb){
target=_target;
}
int resolve(){
if (target){
MTGCardInstance * _target = (MTGCardInstance *)target;
if (nb>0){
for (int i=0; i < nb; i++){
_target->counters->addCounter(power, toughness);
}
}else{
for (int i=0; i < nb; i++){
_target->counters->removeCounter(power, toughness);
}
}
return nb;
}
return 0;
}
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;
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 something when comes into play (should be extended)
class MayAbility:public MTGAbility{
public:
int triggered;
MTGAbility * ability;
MTGAbility * mClone;
MayAbility(int _id, MTGAbility * _ability, MTGCardInstance * _source):MTGAbility(_id,_source),ability(_ability){
triggered = 0;
mClone = NULL;
}
void Update(float dt){
MTGAbility::Update(dt);
if (!triggered){
triggered = 1;
game->mLayers->actionLayer()->setMenuObject(source);
game->mLayers->stackLayer()->setIsInterrupting(source->controller());
OutputDebugString("SetMenuObject!\n");
}
}
const char * getMenuText(){
return ability->getMenuText();
}
int testDestroy(){
if (triggered && !game->mLayers->actionLayer()->menuObject && game->mLayers->actionLayer()->getIndexOf(mClone) ==-1){
OutputDebugString("Destroy!\n");
return 1;
}
return 0;
}
int isReactingToTargetClick(Targetable * card){
OutputDebugString("IsReacting ???\n");
if (card == source) return 1;
return 0;
}
int reactToTargetClick(Targetable * object){
OutputDebugString("ReactToTargetClick!\n");
mClone = ability->clone();
mClone->addToGame();
mClone->forceDestroy = 1;
return mClone->reactToTargetClick(object);
}
~MayAbility(){
if (!isClone) SAFE_DELETE(ability);
}
MayAbility * clone() const{
MayAbility * a = NEW MayAbility(*this);
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(){
vector<int>::size_type sz = abilities.size();
for (unsigned int i = 0; i < sz; i++){
delete abilities[i];
}
}
MultiAbility * clone() const{
MultiAbility * a = NEW MultiAbility(*this);
a->isClone = 1;
return a;
}
};
//Generic Activated Ability
class GenericActivatedAbility:public ActivatedAbility{
public:
MTGAbility * ability;
int limitPerTurn;
int counters;
GenericActivatedAbility(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int _tap = 0, int limit = 0):ActivatedAbility(_id, card,_cost,0,_tap),ability(a),limitPerTurn(limit){
counters = 0;
target = ability->target;
}
int resolve(){
counters++;
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->isClone = 1;
return a;
}
~GenericActivatedAbility(){
if (!isClone){
SAFE_DELETE(ability);
}
}
};
/* Generic TargetAbility */
class GenericTargetAbility:public TargetAbility{
public:
int limitPerTurn;
int counters;
GenericTargetAbility(int _id, MTGCardInstance * _source, TargetChooser * _tc,MTGAbility * a, ManaCost * _cost = NULL, int _tap=0, int limit = 0):TargetAbility(_id,_source, _tc,_cost,0,_tap),limitPerTurn(limit){
ability = a;
counters = 0;
}
GenericTargetAbility * clone() const{
GenericTargetAbility * a = NEW GenericTargetAbility(*this);
a->isClone = 1;
return a;
}
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);
}
};
class ActivatedAbilityTP:public ActivatedAbility{
public:
int who;
ActivatedAbilityTP(int id, MTGCardInstance * card, Targetable * _target = NULL, ManaCost * cost=NULL, int doTap = 0, int who = TargetChooser::UNSET):ActivatedAbility(id,card,cost,0,doTap),who(who){
if (_target) target = _target;
}
Targetable * getTarget(){
switch(who){
case TargetChooser::TARGET_CONTROLLER:
if (target) return ((MTGCardInstance *)target)->controller();
return NULL;
case TargetChooser::CONTROLLER:
return source->controller();
case TargetChooser::OPPONENT:
return source->controller()->opponent();
default:
return target;
}
return NULL;
}
};
//Drawer, allows to draw a card for a cost:
class AADrawer:public ActivatedAbilityTP{
public:
int nbcards;
AADrawer(int _id, MTGCardInstance * card,Targetable * _target,ManaCost * _cost, int _nbcards = 1, 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);
game->mLayers->stackLayer()->resolve();
}
return 1;
}
const char * getMenuText(){
return "Draw";
}
AADrawer * clone() const{
AADrawer * a = NEW AADrawer(*this);
a->isClone = 1;
return a;
}
};
/*Gives life to target controller*/
class AALifer:public ActivatedAbilityTP{
public:
int life;
AALifer(int _id, MTGCardInstance * card, Targetable * _target, int 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;
}
return 1;
}
const char * getMenuText(){
return "Life";
}
AALifer * clone() const{
AALifer * a = NEW AALifer(*this);
a->isClone = 1;
return a;
}
};
class ATokenCreator:public ActivatedAbility{
public:
list<int>abilities;
list<int>types;
list<int>colors;
int power, toughness;
string name;
int multiplier;
ATokenCreator(int _id,MTGCardInstance * _source,ManaCost * _cost, string sname, string stypes,int _power,int _toughness, string sabilities, int _doTap, int _multiplier = 1):ActivatedAbility(_id,_source,_cost,0,_doTap){
power = _power;
toughness = _toughness;
name = sname;
multiplier = _multiplier;
//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++){
unsigned int found = sabilities.find(Constants::MTGBasicAbilities[j]);
if (found != string::npos){
abilities.push_back(j);
}
}
for (int j = 0; j < Constants::MTG_NB_COLORS; j++){
unsigned int found = sabilities.find(Constants::MTGColorStrings[j]);
if (found != string::npos){
colors.push_back(j);
}
}
string s = stypes;
while (s.size()){
unsigned int found = s.find(" ");
if (found != string::npos){
int id = Subtypes::subtypesList->Add(s.substr(0,found));
types.push_back(id);
s = s.substr(found+1);
}else{
int id = Subtypes::subtypesList->Add(s);
types.push_back(id);
s = "";
}
}
}
int resolve(){
for (int i = 0; i < multiplier; ++i){
Token * 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->stack->addCard(myToken);
Spell * spell = NEW Spell(myToken);
spell->resolve();
delete spell;
}
return 1;
}
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;
}
};
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;
}
int resolve(){
MTGCardInstance * _target = (MTGCardInstance *) target;
if(target){
Player* p = _target->controller();
if (p){
GameObserver * g = GameObserver::GetInstance();
MTGGameZone * fromZone = _target->getCurrentZone();
MTGGameZone * destZone = MTGGameZone::stringToZone(destination, source,_target);
//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->stack);
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;
}
};
//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;
}
};
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;
}
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 resolve(){
MTGAbility * a = ability->clone();
a->target = target;
a->addToGame();
return 1;
}
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);
}
};
/*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;
}
};
//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;
}
};
//Allows to untap target card once per turn for a manaCost
class AUntaperOnceDuringTurn:public AUnBlocker{
public:
int untappedThisTurn;
int onlyPlayerTurn;
AUntaperOnceDuringTurn(int id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost, int _onlyPlayerTurn = 1):AUnBlocker(id, _source, _target, _cost){
onlyPlayerTurn = _onlyPlayerTurn;
untappedThisTurn = 0;
}
void Update(float dt){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UNTAP) untappedThisTurn = 0;
AUnBlocker::Update(dt);
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
if (onlyPlayerTurn && game->currentPlayer!=source->controller()) return 0;
if (untappedThisTurn) return 0;
return AUnBlocker::isReactingToClick(card,mana);
}
int reactToClick(MTGCardInstance * card){
untappedThisTurn = 1;
return AUnBlocker::reactToClick(card);
}
virtual ostream& toString(ostream& out) const
{
out << "AUntaperOnceDuringTurn ::: untappedThisTurn : " << untappedThisTurn
<< " ; onlyPlayerTurn : " << onlyPlayerTurn
<< " (";
return AUnBlocker::toString(out) << ")";
}
AUntaperOnceDuringTurn * clone() const{
AUntaperOnceDuringTurn * a = NEW AUntaperOnceDuringTurn(*this);
a->isClone = 1;
return a;
}
};
//Alteration of Power and Toughness (enchantments)
class APowerToughnessModifier: public MTGAbility{
public:
int power, toughness;
APowerToughnessModifier(int id, MTGCardInstance * _source, MTGCardInstance * _target, int _power, int _toughness):MTGAbility(id,_source,_target),power(_power),toughness(_toughness){
}
int addToGame(){
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->power += power;
_target->addToToughness(toughness);
return MTGAbility::addToGame();
}
int destroy(){
((MTGCardInstance *)target)->power -= power;
((MTGCardInstance *)target)->addToToughness(-toughness);
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "APowerToughnessModifier ::: power : " << power
<< " ; toughness : " << toughness
<< " (";
return MTGAbility::toString(out) << ")";
}
APowerToughnessModifier * clone() const{
APowerToughnessModifier * a = NEW APowerToughnessModifier(*this);
a->isClone = 1;
return a;
}
~APowerToughnessModifier(){
OutputDebugString ("DELETING POWERTOUGHNESS");
}
};
// Permanent life alteration evry turn of the target's controller. Useful only for unstable mutation currently
class APowerToughnessModifierRegularCounter:public MTGAbility{
public:
int power, toughness;
int phase;
APowerToughnessModifierRegularCounter(int id, MTGCardInstance * _source, MTGCardInstance * _target, int _phase, int _power, int _toughness):MTGAbility(id,_source,_target),power(_power),toughness(_toughness), phase(_phase){
}
void Update(float dt){
if (newPhase !=currentPhase && newPhase==phase && game->currentPlayer==((MTGCardInstance *)target)->controller()){
((MTGCardInstance *)target)->power += power;
((MTGCardInstance *)target)->addToToughness(toughness);
}
}
virtual ostream& toString(ostream& out) const
{
out << "APowerToughnessModifierRegularCounter ::: power : " << power
<< " ; toughness : " << toughness
<< " ; phase : " << phase
<< " (";
return MTGAbility::toString(out) << ")";
}
APowerToughnessModifierRegularCounter * clone() const{
APowerToughnessModifierRegularCounter * a = NEW APowerToughnessModifierRegularCounter(*this);
a->isClone = 1;
return a;
}
};
//Alteration of Power and Toughness until end of turn (TargetAbility)
// Gives +n/+m until end of turn to any card that's a target
class ATargetterPowerToughnessModifierUntilEOT: public TargetAbility{
public:
MTGCardInstance * mTargets[50];
int nbTargets;
int power, toughness;
ATargetterPowerToughnessModifierUntilEOT(int _id, MTGCardInstance * _source, int _power, int _toughness, ManaCost * _cost, TargetChooser * _tc = NULL, int doTap=1):TargetAbility(_id,_source,_tc,_cost,0,doTap),power(_power),toughness(_toughness){
if (!tc) tc = NEW CreatureTargetChooser(_source);
nbTargets = 0;
}
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->power-=power;
mTarget->addToToughness(-toughness);
}
}
nbTargets = 0;
}
TargetAbility::Update(dt);
}
int resolve(){
MTGCardInstance * mTarget = tc->getNextCardTarget();
if (mTarget){
mTargets[nbTargets] = mTarget;
mTarget->power+= power;
mTarget->addToToughness(toughness);
nbTargets++;
}
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "ATargetterPowerToughnessModifierUntilEOT ::: mTargets : " << mTargets
<< " ; nbTargets : " << nbTargets
<< " ; power : " << power
<< " ; toughness : " << toughness
<< " (";
return TargetAbility::toString(out) << ")";
}
ATargetterPowerToughnessModifierUntilEOT * clone() const{
ATargetterPowerToughnessModifierUntilEOT * a = NEW ATargetterPowerToughnessModifierUntilEOT(*this);
a->isClone = 1;
return a;
}
};
//Alteration of Power and toughness until end of turn (instant)
class AInstantPowerToughnessModifierUntilEOT: public InstantAbility{
public:
int power, toughness;
AInstantPowerToughnessModifierUntilEOT(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _power, int _toughness): InstantAbility(_id, _source, _target), power(_power), toughness(_toughness){
}
int resolve(){
((MTGCardInstance *)target)->power +=power;
((MTGCardInstance *)target)->addToToughness(toughness);
return 1;
}
int destroy(){
((MTGCardInstance *)target)->power -=power;
((MTGCardInstance *)target)->addToToughness(-toughness);
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "APowerToughnessModifierUntilEndOfTurn ::: power : " << power
<< " ; toughness : " << toughness
<< " (";
return InstantAbility::toString(out) << ")";
}
AInstantPowerToughnessModifierUntilEOT * clone() const{
AInstantPowerToughnessModifierUntilEOT * a = NEW AInstantPowerToughnessModifierUntilEOT(*this);
a->isClone = 1;
return a;
}
};
//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, int _power, int _toughness, ManaCost * _cost = NULL, int _maxcounters = 0):ActivatedAbility(id,_source,_cost,0,0),maxcounters(_maxcounters){
counters = 0;
target=_target;
ability = NEW AInstantPowerToughnessModifierUntilEOT(id,_source,_target,_power,_toughness);
}
void Update(float dt){
if (newPhase != currentPhase && newPhase ==Constants::MTG_PHASE_AFTER_EOT){
counters = 0;
}
ActivatedAbility::Update(dt);
}
int fireAbility(){
return resolve();
}
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;
}
APowerToughnessModifierUntilEndOfTurn * clone() const{
APowerToughnessModifierUntilEndOfTurn * a = NEW APowerToughnessModifierUntilEndOfTurn(*this);
a->isClone = 1;
return a;
}
~APowerToughnessModifierUntilEndOfTurn(){
if (!isClone) SAFE_DELETE(ability);
}
};
//Untap Blockers with simple Mana Mechanism
class AUntapManaBlocker: public UntapBlocker{
public:
AUntapManaBlocker(int id, MTGCardInstance * card, ManaCost * _cost):UntapBlocker(id, card, _cost){
}
AUntapManaBlocker(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost):UntapBlocker(id, card,_target, _cost){
}
virtual ostream& toString(ostream& out) const
{
out << "AUntapManaBlocker ::: (";
return UntapBlocker::toString(out) << ")";
}
AUntapManaBlocker * clone() const{
AUntapManaBlocker * a = NEW AUntapManaBlocker(*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);
}
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;
}
};
/*Gives protection to a target */
class AProtectionFrom:public MTGAbility{
public:
CardDescriptor * cd;
void initProtection(){
((MTGCardInstance *)target)->addProtection(cd);
}
AProtectionFrom(int _id, MTGCardInstance * _source, MTGCardInstance * _target, CardDescriptor * _cd):MTGAbility(_id, _source, _target),cd(_cd){
initProtection();
}
AProtectionFrom(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int color):MTGAbility(_id, _source, _target){
cd = NEW CardDescriptor();
cd->colors[color] = 1;
initProtection();
}
int destroy(){
((MTGCardInstance *)target)->removeProtection(cd);
return 1;
}
~AProtectionFrom(){
delete(cd);
}
virtual ostream& toString(ostream& out) const
{
out << "AProtectionFrom ::: cd : " << cd
<< " (";
return MTGAbility::toString(out) << ")";
}
AProtectionFrom * clone() const{
AProtectionFrom * a = NEW AProtectionFrom(*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 ListMaintainerAbility{
public:
int power, toughness;
MTGCardInstance * luckyWinner;
AExalted(int _id, MTGCardInstance * _source, int _power = 1, int _toughness = 1):ListMaintainerAbility(_id, _source),power(_power),toughness(_toughness){
luckyWinner = NULL;
}
int canBeInList(MTGCardInstance * card){
if (card->isAttacker() && game->currentPlayer == source->controller() && game->isInPlay(card)) return 1;
return 0;
}
int added(MTGCardInstance * card){
if(cards.size() == 1){
luckyWinner = cards.begin()->first;
luckyWinner->addToToughness(toughness);
luckyWinner->power+=power;
}else if (cards.size() == 2){
luckyWinner->addToToughness(-toughness);
luckyWinner->power-=power;
}
return 1;
}
int removed(MTGCardInstance * card){
if(cards.size() == 1){
luckyWinner = cards.begin()->first;
luckyWinner->addToToughness(toughness);
luckyWinner->power+=power;
}else if (cards.size() == 0){
luckyWinner->addToToughness(-toughness);
luckyWinner->power-=power;
}
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AExalted ::: power : " << power
<< " ; toughness : " << toughness
<< " (";
return ListMaintainerAbility::toString(out) << ")";
}
AExalted * clone() const{
AExalted * a = NEW AExalted(*this);
a->isClone = 1;
return a;
}
};
//ExaltedAbility for basic abilities (Shards of Alara)
class AExaltedAbility:public ListMaintainerAbility{
public:
int ability;
MTGCardInstance * luckyWinner;
AExaltedAbility(int _id, MTGCardInstance * _source, int _ability):ListMaintainerAbility(_id, _source),ability(_ability){
luckyWinner = NULL;
}
int canBeInList(MTGCardInstance * card){
if (card->isAttacker() && game->currentPlayer == source->controller() && game->isInPlay(card)) return 1;
return 0;
}
int added(MTGCardInstance * card){
luckyWinner = cards.begin()->first;
if(cards.size() == 1){
luckyWinner->basicAbilities[ability]+=1;
}else if (cards.size() == 2){
luckyWinner->basicAbilities[ability]-=1;
}
return 1;
}
int removed(MTGCardInstance * card){
if(cards.size() == 1){
luckyWinner->basicAbilities[ability]+=1;
}else if (cards.size() == 0){
luckyWinner->basicAbilities[ability]-=1;
}
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AExaltedAbility ::: ability : " << ability
<< " ; luckyWinner : " << luckyWinner
<< " (";
return ListMaintainerAbility::toString(out) << ")";
}
AExaltedAbility * clone() const{
AExaltedAbility * a = NEW AExaltedAbility(*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->Add(_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:
MTGAbility * ability;
MTGAbility * a;
int includeSelf;
AAsLongAs(int _id, MTGCardInstance * _source, TargetChooser * _tc, int _includeSelf, MTGAbility * a):ListMaintainerAbility(_id, _source),ability(a){
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();
cards.clear();
players.clear();
return 1;
}
int added(MTGCardInstance * card){
if (cards.size()== 1){
a = ability->clone();
a->addToGame();
return 1;
}
return 0;
}
int removed(MTGCardInstance * card){
if (cards.size()== 0){
game->removeObserver(a);
a = NULL;
return 1;
}
return 0;
}
~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:
MTGAbility * ability;
int includeSelf;
map<Damageable *, MTGAbility *> abilities;
ALord(int _id, MTGCardInstance * card, TargetChooser * _tc, int _includeSelf, MTGAbility * a):ListMaintainerAbility(_id,card), ability(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;
}
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:
MTGAbility * ability;
int includeSelf;
map<Damageable *, MTGAbility *> abilities;
AForeach(int _id, MTGCardInstance * card,MTGCardInstance * _target, TargetChooser * _tc, int _includeSelf, MTGAbility * a):ListMaintainerAbility(_id,card,_target), ability(a){
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){
MTGAbility * a = ability->clone();
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 AADamager:public ActivatedAbilityTP{
public:
int damage;
AADamager(int _id, MTGCardInstance * _source, Targetable * _target, int _damage = 0, ManaCost * _cost=NULL, int doTap = 0, int who = TargetChooser::UNSET):ActivatedAbilityTP(_id,_source,_target,_cost,doTap,who),damage(_damage){
aType = MTGAbility::DAMAGER;
}
int resolve(){
if(target){
Damageable * _target = (Damageable *) getTarget();
game->mLayers->stackLayer()->addDamage(source,_target, damage);
game->mLayers->stackLayer()->resolve();
return 1;
}
return 0;
}
const char * getMenuText(){
return "Damage";
}
AADamager * clone() const{
AADamager * a = NEW AADamager(*this);
a->isClone = 1;
return a;
}
};
/* Standard Damager, can choose a NEW target each time the price is paid */
class TADamager:public TargetAbility{
public:
int damage;
TADamager(int id, MTGCardInstance * card, ManaCost * _cost, int _damage, TargetChooser * _tc = NULL, int _tap = 0):TargetAbility(id,card, _tc, _cost,0,_tap),damage(_damage){
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++){
game->mLayers->stackLayer()->addPutInGraveyard(opponents[i]);
}
}
}
}
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;
}
};
//Converts a card to a creature (Aura)
class AConvertToCreatureAura:public MTGAbility{
public:
AConvertToCreatureAura(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _power, int _toughness):MTGAbility(_id, _source, _target){
_target->setSubtype("creature");
_target->power = _power;
_target->toughness = _toughness;
_target->life = _toughness;
//_target->afterDamage();
_target->doDamageTest = 1;
}
int destroy(){
MTGCardInstance * _target = (MTGCardInstance *) target;
_target->removeType("creature");
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AConvertToCreatureAura ::: (";
return MTGAbility::toString(out) << ")";
}
AConvertToCreatureAura * clone() const{
AConvertToCreatureAura * a = NEW AConvertToCreatureAura(*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;
}
};
//Ankh of Mishra
class AAnkhOfMishra: public ListMaintainerAbility{
public:
int init;
AAnkhOfMishra(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("land") && game->isInPlay(card)) return 1;
return 0;
}
int added(MTGCardInstance * card){
if (!init) return 0;
game->mLayers->stackLayer()->addDamage(source,card->controller(), 2);
return 1;
}
int removed(MTGCardInstance * card){
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AAnkhOfMishra ::: init : " << init
<< " (";
return ListMaintainerAbility::toString(out) << ")";
}
AAnkhOfMishra * clone() const{
AAnkhOfMishra * a = NEW AAnkhOfMishra(*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;
}
};
//Channel
class AChannel:public ActivatedAbility{
public:
AChannel(int _id, MTGCardInstance * card):ActivatedAbility(_id, card,0,0,0){
}
int isReactingToClick(PlayGuiObject * object){
if (object->type == GUI_AVATAR){
Player * player = ((GuiAvatar *)object)->player;
if (player == source->controller()) return 1;
}
return 0;
}
int resolve(){
source->controller()->life--;
source->controller()->getManaPool()->add(Constants::MTG_COLOR_ARTIFACT, 1);
return 1;
}
int testDestroy(){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UNTAP) return 1;
currentPhase = newPhase;
return 0;
}
virtual ostream& toString(ostream& out) const
{
out << "AChannel ::: (";
return ActivatedAbility::toString(out) << ")";
}
AChannel * clone() const{
AChannel * a = NEW AChannel(*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;
}
};
//Creature bond
class ACreatureBond:public MTGAbility{
public:
int resolved;
ACreatureBond(int _id, MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_id,_source,_target){
resolved = 0;
}
int receiveEvent(WEvent * event){
MTGCardInstance * _target = (MTGCardInstance *) target;
if (event->type == WEvent::CHANGE_ZONE){
WEventZoneChange * e = (WEventZoneChange *) event;
MTGCardInstance * card = e->card->previous;
if (card == _target){
for (int i = 0; i < 2 ; i++){
Player * p = game->players[i];
if (e->to == p->game->graveyard){
game->mLayers->stackLayer()->addDamage(source,_target->controller(),_target->toughness);
return 1;
}
}
}
}
return 0;
}
virtual ostream& toString(ostream& out) const
{
out << "ACreatureBond ::: resolved : " << resolved
<< " (";
return MTGAbility::toString(out) << ")";
}
ACreatureBond * clone() const{
ACreatureBond * a = NEW ACreatureBond(*this);
a->isClone = 1;
return a;
}
};
//1105: Dingus Egg
class ADingusEgg: public ListMaintainerAbility{
public:
ADingusEgg(int id, MTGCardInstance * _source):ListMaintainerAbility(id, _source){
}
int canBeInList(MTGCardInstance * card){
if (card->hasType("land") && game->isInPlay(card)) return 1;
return 0;
}
int added(MTGCardInstance * card){
return 1;
}
int removed(MTGCardInstance * card){
game->mLayers->stackLayer()->addDamage(source,card->controller(), 2);
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "ADingusEgg ::: (";
return ListMaintainerAbility::toString(out) << ")";
}
ADingusEgg * clone() const{
ADingusEgg * a = NEW ADingusEgg(*this);
a->isClone = 1;
return a;
}
};
//1106 DisruptingScepter
class ADisruptingScepter:public TargetAbility{
public:
ADisruptingScepter(int id, MTGCardInstance * _source):TargetAbility(id,_source){
int zones[] = {MTGGameZone::OPPONENT_HAND};
tc = NEW TargetZoneChooser(zones,1,_source);
int _cost[] = {Constants::MTG_COLOR_ARTIFACT, 3};
cost = NEW ManaCost(_cost,1);
}
void Update(float dt){
if (game->opponent()->isAI()){
if(waitingForAnswer){
MTGCardInstance * card = ((AIPlayer *)game->opponent())->chooseCard(tc, source);
if (card) tc->toggleTarget(card);
if (!card || tc->targetsReadyCheck() == TARGET_OK) waitingForAnswer = 0;
}
TargetAbility::Update(dt);
}else{
TargetAbility::Update(dt);
}
}
int resolve(){
game->opponent()->game->putInGraveyard(tc->getNextCardTarget());
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "ADisruptingScepter ::: (";
return TargetAbility::toString(out) << ")";
}
ADisruptingScepter * clone() const{
ADisruptingScepter * a = NEW ADisruptingScepter(*this);
a->isClone = 1;
return a;
}
};
//1108 Ebony Horse
class AEbonyHorse:public TargetAbility{
public:
AEbonyHorse(int _id, MTGCardInstance * _source):TargetAbility(_id,_source, NEW CreatureTargetChooser()){
int _cost[] = {Constants::MTG_COLOR_ARTIFACT, 2};
cost = NEW ManaCost(_cost,1);
}
int resolve(){
MTGCardInstance * _target = tc->getNextCardTarget();
if (_target->isAttacker()) _target->toggleAttacker();
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AEbonyHorse ::: (";
return TargetAbility::toString(out) << ")";
}
AEbonyHorse * clone() const{
AEbonyHorse * a = NEW AEbonyHorse(*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;
}
};
//1110 Glasses of Urza
class AGlassesOfUrza:public MTGAbility{
public:
CardDisplay * display;
bool isActive;
AGlassesOfUrza(int _id, MTGCardInstance * _source):MTGAbility(_id, _source),isActive(false){
display = NEW CardDisplay(0, game,SCREEN_WIDTH/2, SCREEN_HEIGHT/2,NULL);
}
void Update(float dt){
if(isActive){
display->Update(dt);
}
}
bool CheckUserInput(u32 key){
if (isActive){
if (display->CheckUserInput(key)) return true;
if (PSP_CTRL_CROSS == key){
isActive = false;
return true;
}
}
return false;
}
void Render(float dt){
if (isActive){
display->Render();
}
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
if ( card == source){
if (game->currentlyActing()->game->isInPlay(card) && !source->isTapped()){
return 1;
}
}
return 0;
}
int reactToClick(MTGCardInstance * card){
if (!isReactingToClick(card)) return 0;
source->tap();
isActive = true;
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AGlassesOfUrza ::: display : " << display
<< " ; isActive : " << isActive
<< " (";
return MTGAbility::toString(out) << ")";
}
AGlassesOfUrza * clone() const{
AGlassesOfUrza * a = NEW AGlassesOfUrza(*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;
}
};
//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;
}
void Update(float dt){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UNTAP) usedThisTurn = 0;
Damage * damage = ((Damage *)game->mLayers->stackLayer()->getNext(latest,ACTION_DAMAGE,RESOLVED_OK));
while (damage){
if (damage->target == source->controller()){
counters += damage->damage;
}
latest = damage;
damage = ((Damage *)game->mLayers->stackLayer()->getNext(damage,ACTION_DAMAGE,RESOLVED_OK));
}
}
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;
}
};
//Lord of the Pit
class ALordOfThePit: public TargetAbility{
public:
int paidThisTurn;
ALordOfThePit(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(), 7);
}
}
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 << "ALordOfThePit ::: paidThisTurn : " << paidThisTurn
<< " (";
return TargetAbility::toString(out) << ")";
}
ALordOfThePit * clone() const{
ALordOfThePit * a = NEW ALordOfThePit(*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->stack);
Spell * spell = NEW Spell(copy);
//af.addAbilities(game->mLayers->actionLayer()->getMaxId(), spell);
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 init;
int dealDamage;
AErgRaiders(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
init = 0;
dealDamage = 0;
}
void Update(float dt){
if (newPhase != currentPhase){
Player * controller = source->controller();
if (newPhase == Constants::MTG_PHASE_COMBATDAMAGE && game->currentPlayer == controller){
if (!source->isAttacker() && init){
dealDamage = 1;
}
}else if (newPhase == Constants::MTG_PHASE_UNTAP && game->currentPlayer != controller){
if (dealDamage){
game->mLayers->stackLayer()->addDamage(source, controller,2);
}
init = 1;
dealDamage = 0;
}
}
}
virtual ostream& toString(ostream& out) const
{
out << "AErgRaiders ::: init : " << init
<< " ; dealDamage : " << dealDamage
<< " (";
return MTGAbility::toString(out) << ")";
}
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;
}
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);
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:
int nbdamagesthisturn[2];
AHypnoticSpecter(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
currentPhase = -1;
for (int i = 0; i < 2; i++){
nbdamagesthisturn[i] = 0;
}
}
void Update(float dt){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UNTAP){
for (int i = 0; i < 2; i++){
nbdamagesthisturn[i] = 0;
}
}
ActionStack * as = game->mLayers->stackLayer();
int nbdamages[2];
for (int i = 0; i < 2; i++){
nbdamages[i] = 0;
}
Damage * current = ((Damage *)as->getNext(NULL,ACTION_DAMAGE,RESOLVED_OK));
while(current){
if (current->source == source){
for (int j=0; j < 2; j++){
if(current->target == game->players[j]) nbdamages[j]++;
}
}
current = ((Damage *)as->getNext(current,ACTION_DAMAGE,RESOLVED_OK));
}
for (int i = 0; i < 2; i++){
while(nbdamages[i] > nbdamagesthisturn[i]){
nbdamagesthisturn[i]++;
game->players[i]->game->discardRandom(game->players[i]->game->hand);
}
}
}
virtual ostream& toString(ostream& out) const
{
out << "AHypnoticSpecter ::: nbdamagesthisturn : " << nbdamagesthisturn
<< " (";
return MTGAbility::toString(out) << ")";
}
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:
int previouslyTapped;
AKudzu(int _id, MTGCardInstance * card, MTGCardInstance * _target):TargetAbility(_id,card, NEW TypeTargetChooser("land",card)){
tc->toggleTarget(_target);
target = _target;
previouslyTapped = 0;
if (_target->isTapped()) previouslyTapped = 1;
}
void Update(float dt){
MTGCardInstance * _target = (MTGCardInstance *)target;
if (_target && !_target->isTapped()){
previouslyTapped = 0;
}else if (!previouslyTapped){
#if defined (WIN32) || defined (LINUX)
OutputDebugString("Kudzu Strikes !\n");
#endif
MTGCardInstance * _target = (MTGCardInstance *)target;
target = _target->controller()->game->putInGraveyard(_target);
reactToClick(source); // ????
}
TargetAbility::Update(dt);
}
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;
previouslyTapped = 0;
if (source->target && source->target->isTapped()) previouslyTapped = 1;
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;
}
virtual ostream& toString(ostream& out) const
{
out << "AKudzu ::: previouslyTapped : " << previouslyTapped
<< " (";
return TargetAbility::toString(out) << ")";
}
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]->isACreature()) 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;
}
};
//Power Surge
class APowerSurge:public TriggeredAbility{
public:
int totalLands;
APowerSurge(int _id, MTGCardInstance * _source):TriggeredAbility(_id,_source){
totalLands = 0;
}
int trigger(){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_EOT){
//That's ugly but untapped land at the beginning of the turn are opponent's untapped lands at the end of the turn
totalLands = 0;
MTGInPlay * inPlay = game->opponent()->game->inPlay;
for (int i = 0; i < inPlay->nb_cards; i++){
MTGCardInstance * card = inPlay->cards[i];
if (!card->isTapped() && card->hasType("land")){
totalLands++;
}
}
}
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UPKEEP && totalLands){
return 1;
}
return 0;
}
int resolve(){
if (totalLands) game->mLayers->stackLayer()->addDamage(source,game->currentPlayer,totalLands);
totalLands = 0;
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "APowerSurge ::: totalLands : " << totalLands
<< " (";
return TriggeredAbility::toString(out) << ")";
}
APowerSurge * clone() const{
APowerSurge * a = NEW APowerSurge(*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;
}
};
//1218 Psychic Venom
class APsychicVenom:public MTGAbility{
public:
int tapped;
APsychicVenom(int _id, MTGCardInstance * _source, MTGCardInstance * _target):MTGAbility(_id, _source,_target){
tapped = _target->isTapped();
}
void Update(float dt){
MTGCardInstance* _target = (MTGCardInstance* )target;
int newState = _target->isTapped();
if (newState != tapped && newState == 1){
game->mLayers->stackLayer()->addDamage(source,_target->controller(),2);
}
tapped = newState;
}
virtual ostream& toString(ostream& out) const
{
out << "APsychicVenom ::: tapped : " << tapped
<< " (";
return MTGAbility::toString(out) << ")";
}
APsychicVenom * clone() const{
APsychicVenom * a = NEW APsychicVenom(*this);
a->isClone = 1;
return a;
}
};
//1221 Serendib Efreet
class ASerendibEfreet:public MTGAbility{
public:
ASerendibEfreet(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){
}
void Update(float dt){
if (newPhase == Constants::MTG_PHASE_UPKEEP && newPhase != currentPhase && game->currentPlayer == source->controller()){
game->mLayers->stackLayer()->addDamage(source,game->currentPlayer,1);
}
}
virtual ostream& toString(ostream& out) const
{
out << "ASerendibEfreet ::: (";
return MTGAbility::toString(out) << ")";
}
ASerendibEfreet * clone() const{
ASerendibEfreet * a = NEW ASerendibEfreet(*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;
}
};
//1276 Wanderlust, 1148 Cursed Lands
class AWanderlust:public TriggeredAbility{
public:
AWanderlust(int _id, MTGCardInstance * _source, MTGCardInstance * _target):TriggeredAbility(_id,_source, _target){}
int trigger(){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UPKEEP && ((MTGCardInstance *) target)->controller()==game->currentPlayer){
return 1;
}
return 0;
}
int resolve(){
game->mLayers->stackLayer()->addDamage(source,((MTGCardInstance *) target)->controller(),1);
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AWanderlust ::: (";
return TriggeredAbility::toString(out) << ")";
}
AWanderlust * clone() const{
AWanderlust * a = NEW AWanderlust(*this);
a->isClone = 1;
return a;
}
};
//1284 Dragon Whelp
class ADragonWhelp: public APowerToughnessModifierUntilEndOfTurn{
public:
ADragonWhelp(int id, MTGCardInstance * card):APowerToughnessModifierUntilEndOfTurn(id, card, card, 1, 0, NEW ManaCost()){
cost->add(Constants::MTG_COLOR_RED, 1);
}
void Update(float dt){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UNTAP && 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->cursor;
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;
}
};
//1245 ForceOfNature
class AForceOfNature:public ActivatedAbility{
public:
int dealDamageThisTurn;
AForceOfNature(int _id, MTGCardInstance * card):ActivatedAbility(_id,card, NEW ManaCost(),1,0){
dealDamageThisTurn = 0;
cost->add(Constants::MTG_COLOR_GREEN,4);
}
void Update(float dt){
if (newPhase !=currentPhase){
if (newPhase == Constants::MTG_PHASE_UNTAP){
dealDamageThisTurn = 1;
}else if (newPhase == Constants::MTG_PHASE_DRAW && dealDamageThisTurn && game->currentPlayer==source->controller() ){
game->mLayers->stackLayer()->addDamage(source,source->controller(),8);
}
}
ActivatedAbility::Update(dt);
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
return (dealDamageThisTurn && currentPhase == Constants::MTG_PHASE_UPKEEP && ActivatedAbility::isReactingToClick(card,mana));
}
int resolve(){
dealDamageThisTurn = 0;
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AForceOfNature ::: dealDamageThisTurn : " << dealDamageThisTurn
<< " (";
return ActivatedAbility::toString(out) << ")";
}
AForceOfNature * clone() const{
AForceOfNature * a = NEW AForceOfNature(*this);
a->isClone = 1;
return a;
}
};
//1309 Orcish Artilery
class AOrcishArtillery: public TADamager{
public:
AOrcishArtillery(int _id,MTGCardInstance * card): TADamager(_id, card, NEW ManaCost(), 2){
}
int resolve(){
TADamager::resolve();
game->mLayers->stackLayer()->addDamage(source,source->controller(), 3);
return 1;
}
AOrcishArtillery * clone() const{
AOrcishArtillery * a = NEW AOrcishArtillery(*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;
}
};
//Soul Net
class ASoulNet:public ActivatedAbility{
public:
PutInGraveyard * latest;
PutInGraveyard * newDead;
ASoulNet(int _id, MTGCardInstance * card):ActivatedAbility(_id, card,0,0,0){
int _cost[] = {Constants::MTG_COLOR_ARTIFACT, 1};
cost = NEW ManaCost(_cost,1);
latest = ((PutInGraveyard *) GameObserver::GetInstance()->mLayers->stackLayer()->getPrevious(NULL,ACTION_PUTINGRAVEYARD,RESOLVED_OK));
newDead = latest;
}
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
newDead = ((PutInGraveyard *) GameObserver::GetInstance()->mLayers->stackLayer()->getPrevious(NULL,ACTION_PUTINGRAVEYARD,RESOLVED_OK));
if (newDead && newDead != latest && newDead->card->isACreature())
return ActivatedAbility::isReactingToClick(card,mana);
return 0;
}
int resolve(){
latest = newDead;
source->controller()->life++;
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "ASoulNet ::: latest : " << latest
<< " ; newDead : " << newDead
<< " (";
return ActivatedAbility::toString(out) << ")";
}
ASoulNet * clone() const{
ASoulNet * a = NEW ASoulNet(*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;
}
};
//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;
}
};
// Generic Karma
class ADamageForTypeControlled: public TriggeredAbility{
public:
char type[20];
ADamageForTypeControlled(int _id, MTGCardInstance * _source,const char * _type):TriggeredAbility(_id, _source){
sprintf(type,"%s",_type);
}
int trigger(){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UPKEEP) return 1;
return 0;
}
int resolve(){
int totaldamage = 0;
MTGGameZone * zone = game->currentPlayer->game->inPlay;
for (int i = 0; i < zone->nb_cards; i++){
if (zone->cards[i]->hasType(type)) totaldamage++;;
}
if (totaldamage) game->mLayers->stackLayer()->addDamage(source,game->currentPlayer, totaldamage);
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "ADamageForTypeControlled ::: type : " << type
<< " (";
return TriggeredAbility::toString(out) << ")";
}
ADamageForTypeControlled * clone() const{
ADamageForTypeControlled * a = NEW ADamageForTypeControlled(*this);
a->isClone = 1;
return a;
}
};
// Dreamborn Muse
class ADreambornMuse: public TriggeredAbility{
public:
int nbcards;
ADreambornMuse(int _id, MTGCardInstance * _source):TriggeredAbility(_id, _source){
}
int trigger(){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UPKEEP) return 1;
return 0;
}
int resolve(){
int nbcards = game->currentPlayer->game->hand->nb_cards;
MTGLibrary * library = game->currentPlayer->game->library;
for (int i = 0; i < nbcards; i++){
if (library->nb_cards)
game->currentPlayer->game->putInZone(library->cards[library->nb_cards-1],library,game->currentPlayer->game->graveyard);
}
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "ADreambornMuse ::: nbcards : " << nbcards
<< " (";
return TriggeredAbility::toString(out) << ")";
}
ADreambornMuse * clone() const{
ADreambornMuse * a = NEW ADreambornMuse(*this);
a->isClone = 1;
return a;
}
};
//ShieldOfTheAge
class AShieldOfTheAge: public TargetAbility{
public:
AShieldOfTheAge(int _id, MTGCardInstance * card):TargetAbility(_id,card,NEW DamageTargetChooser(card,_id),NEW ManaCost(),0,0){
cost->add(Constants::MTG_COLOR_ARTIFACT,2);
}
int resolve(){
Damage * damage = tc->getNextDamageTarget();
if (!damage) return 0;
game->mLayers->stackLayer()->Fizzle(damage);
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AShieldOfTheAge ::: (";
return TargetAbility::toString(out) << ")";
}
AShieldOfTheAge * clone() const{
AShieldOfTheAge * a = NEW AShieldOfTheAge(*this);
a->isClone = 1;
return a;
}
};
// GiveLifeForTappedType
class AGiveLifeForTappedType:public MTGAbility{
public:
char type[20];
int nbtypestapped;
int counttypesTapped(){
int result = 0;
MTGInPlay * inplay = source->controller()->opponent()->game->inPlay;
for (int i = 0; i < inplay->nb_cards; i++){
MTGCardInstance * card = inplay->cards[i];
if (card->isTapped() && card->hasType(type)) result++;
}
return result;
}
AGiveLifeForTappedType(int _id, MTGCardInstance * source, const char * _type):MTGAbility(_id, source){
sprintf(type,"%s",_type);{
nbtypestapped = counttypesTapped();
}
}
void Update(float dt){
int newcount = counttypesTapped();
for (int i=0; i < newcount - nbtypestapped; i++){
source->controller()->life++;
}
nbtypestapped = newcount;
}
virtual ostream& toString(ostream& out) const
{
out << "AGiveLifeForTappedType ::: type : " << type
<< " ; nbtypestapped : " << nbtypestapped
<< " (";
return MTGAbility::toString(out) << ")";
}
AGiveLifeForTappedType * clone() const{
AGiveLifeForTappedType * a = NEW AGiveLifeForTappedType(*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;
}
void Update(float dt){
if (newPhase != currentPhase){
if( source->isAttacker() && newPhase == Constants::MTG_PHASE_COMBATDAMAGE){
nbOpponents = source->blockers.size();
for (int i = MaxOpponent; i < nbOpponents; i++){
source->power+= PowerModifier;
source->addToToughness(ToughnessModifier);
}
}
if( newPhase == Constants::MTG_PHASE_AFTER_EOT ){
for (int i = MaxOpponent; i < nbOpponents; i++){
source->power-= PowerModifier;
source->addToToughness(-ToughnessModifier);
}
nbOpponents = 0;
}
}
}
ARampageAbility * clone() const{
ARampageAbility * a = NEW ARampageAbility(*this);
a->isClone = 1;
return a;
}
};
// Seedborn Muse
class ASeedbornMuse: public TriggeredAbility{
public:
ASeedbornMuse(int _id, MTGCardInstance * _source):TriggeredAbility(_id, _source){
}
int trigger(){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UPKEEP && ((MTGCardInstance *) source)->controller()!= game->currentPlayer){
return 1;
}
return 0;
}
int resolve(){
for (int j = source->controller()->game->inPlay->nb_cards-1; j >=0 ; j--){
MTGCardInstance * current = source->controller()->game->inPlay->cards[j];
current->untap();
}
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "ASeedbornMuse ::: (";
return TriggeredAbility::toString(out) << ")";
}
ASeedbornMuse * clone() const{
ASeedbornMuse * a = NEW ASeedbornMuse(*this);
a->isClone = 1;
return a;
}
};
// Graveborn Muse
class AGravebornMuse: public TriggeredAbility{
public:
int nbcards_life;
AGravebornMuse(int _id, MTGCardInstance * _source):TriggeredAbility(_id, _source){
nbcards_life=0;
}
int trigger(){
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UPKEEP && ((MTGCardInstance *) source)->controller()== game->currentPlayer){
return 1;
}
return 0;
}
int resolve(){
for (int j = source->controller()->game->inPlay->nb_cards-1; j >=0 ; j--){
MTGCardInstance * current = source->controller()->game->inPlay->cards[j];
if (current->hasSubtype("zombie")){
nbcards_life++;
}
}
source->controller()->life-=nbcards_life;
game->mLayers->stackLayer()->addDraw(source->controller(),nbcards_life);
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "AGravebornMuse ::: nbcards_life : " << nbcards_life
<< " (";
return TriggeredAbility::toString(out) << ")";
}
AGravebornMuse * clone() const{
AGravebornMuse * a = NEW AGravebornMuse(*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();
// TrueController->game->putInZone(_target, TrueController->game->inPlay, source->controller()->game->inPlay);
TheftController = source->controller();
MTGCardInstance * copy = _target->changeController(game->currentlyActing());
target = copy;
source->target = copy;
}
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("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;
}
};
//Life/Damage for type removed/added from game - Generic Ankh of Mishra/dingus Egg
class ALifeModifierPutinplay: public ListMaintainerAbility{
public:
int init;
int life;
int PlayerTarget;
int AddOrRemove;
ALifeModifierPutinplay(int id, MTGCardInstance * _source,TargetChooser * _tc, int _life, int _PlayerTarget, int _AddOrRemove):ListMaintainerAbility(id, _source){
init = 0;
tc = _tc;
PlayerTarget = _PlayerTarget;
AddOrRemove = _AddOrRemove;
life = _life;
}
void Update(float dt){
ListMaintainerAbility::Update(dt);
init = 1;
}
int canBeInList(MTGCardInstance * card){
if (tc->canTarget(card)) return 1;
return 0;
}
int added(MTGCardInstance * card){
if (!init) return 0;
if (AddOrRemove == 1){
if (life < 0){
int damage = life * -1;
if (PlayerTarget == 2){
game->mLayers->stackLayer()->addDamage(source,card->controller(), damage);
}
if (PlayerTarget == 1){
game->mLayers->stackLayer()->addDamage(source,source->controller(), damage);
}
if (PlayerTarget == 0){
game->mLayers->stackLayer()->addDamage(source,source->controller()->opponent(), damage);
}
}
if (life > 0){
if (PlayerTarget == 2){
card->controller()->life+=life;
}
if (PlayerTarget == 1){
source->controller()->life+=life;
}
if (PlayerTarget == 0){
source->controller()->opponent()->life+=life;
}
}
}
return 1;
}
int removed(MTGCardInstance * card){
if (AddOrRemove == 0){
if (life < 0){
int damage = life * -1;
if (PlayerTarget == 2){
game->mLayers->stackLayer()->addDamage(source,card->controller(), damage);
}
if (PlayerTarget == 1){
game->mLayers->stackLayer()->addDamage(source,source->controller(), damage);
}
if (PlayerTarget == 0){
game->mLayers->stackLayer()->addDamage(source,source->controller()->opponent(), damage);
}
}
if (life > 0){
if (PlayerTarget == 2){
card->controller()->life+=life;
}
if (PlayerTarget == 1){
source->controller()->life+=life;
}
if (PlayerTarget == 0){
source->controller()->opponent()->life+=life;
}
}
}
return 1;
}
virtual ostream& toString(ostream& out) const
{
out << "ALifeModifierPutinplay ::: init : " << init
<< " ; life : " << life
<< " ; PlayerTarget : " << PlayerTarget
<< " ; AddOrRemove : " << AddOrRemove
<< " (";
return ListMaintainerAbility::toString(out) << ")";
}
ALifeModifierPutinplay * clone() const{
ALifeModifierPutinplay * a = NEW ALifeModifierPutinplay(*this);
a->isClone = 1;
return a;
}
};
/// Work in Progress also from no on all code could be removed...
///// Not working need to work on this one
///Abomination Kill blocking creature if white or green
class AAbomination :public MTGAbility{
public:
MTGCardInstance * opponents[20];
int nbOpponents;
AAbomination (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->hasColor(Constants::MTG_COLOR_GREEN)) || opponent->hasColor(Constants::MTG_COLOR_WHITE)){
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]);
}
}
}
}
int testDestroy(){
if(!game->isInPlay(source) && currentPhase != Constants::MTG_PHASE_UNTAP){
return 0;
}else{
return MTGAbility::testDestroy();
}
}
virtual ostream& toString(ostream& out) const
{
out << "AAbomination ::: opponents : " << opponents
<< " ; nbOpponents : " << nbOpponents
<< " (";
return MTGAbility::toString(out) << ")";
}
AAbomination * clone() const{
AAbomination * a = NEW AAbomination(*this);
a->isClone = 1;
return a;
}
};
#endif