473abd9814
ok i WAS going to write a full change log with code exsamples ect, but since im rushed you will get the short version of this log.
first bug fixes, and there were many,
indestructible creature bug fixed
halimar execavator *embearessing youtube video" bug is fixed
token text now displays source name and tokens abilities
fixed a card view null pointer in an iterator when code used combinations of foreach and aslongas with CD.
epic struggle bug fixed, aslongas was only parsing one space to the right of the operator.
extra cost containing targetting fixed, cards can now have multiple extra cost in all mana...this includes giving a card 2 targeted sacrifices as its main cost.
angelic chorus bug fixed, the card will be soft coded now.
and many other minor bugs fixed, hard to remember all which were fixed.
now, new abilities = words
"legendarylandwalk",
"desertlandwalk",
"snowforestlandwalk",
"snowplainslandwalk",
"snowmountainlandwalk",
"snowislandlandwalk",
"snowswamplandwalk",
"snowlandwalk",
"nonbasiclandwalk",
"strong",//cant be blocked by creature with less power
"weak",//cant block creatures with more power
"phasing",
all true landwalks will now be supported.
new cost types:
morph which is coded as follows
[card]
name=Bloodstoke Howler
facedown={3}
autofacedown={6}{R}:morph
autofaceup=3/0 all(beast|mybattlefield))
text=Morph {6}{R} (You may cast this face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.) -- When Bloodstoke Howler is turned
face up, Beast creatures you control get +3/+0 until end of turn.
mana={5}{R}
type=Creature
subtype=Beast
power=3
toughness=4
[/card]
you will notice new auto lines autofaceup and autofacedown
these are abilities the cards will have when theyre in that state.
the cost is coded as
facedown={cost}
when a card is faced up it gains auto= lines also.
tho is played normally it will NOT gain autofaceup=lines
card restrictions:
cards can now have restrictions placed on them the restrictions are.
all previous restrictions usable in activated abilities
with the follow additions
control two or more vampires
control less creatures
control snow land
casted a spell
one of a kind
fourth turn
before battle damage
after battle
during battle
[card]
name=Blood Frenzy
target=creature[attacking;blocking]
restriction=before battle damage
auto=4/0
auto=treason
text=Cast Blood Frenzy only before the combat damage step. -- Target attacking or blocking creature gets +4/+0 until end of turn. Destroy that creature
at the beginning of the next end step.
mana={1}{R}
type=Instant
[/card]
other cost now can have specail restrictions also:
otherrestriction=mytypemin:1 type(swamp),opponenttypemin:1 opponenttype(plains)
these are minimums required inplay of a type
it can be just you, or you and opponent or just opponent
you can also use the words "more" and "less" and * to compare the 2 players fields.
[card]
name=Cho-Arrim Legate
abilities=protection from black
other={0}
otherrestriction=mytypemin:1 type(swamp) , opponenttypemin:1 opponenttype(plains)
text=Protection from black -- If an opponent controls a Swamp and you control a Plains, you may cast Cho-Arrim Legate without paying its mana cost.
mana={2}{W}
type=Creature
subtype=Human Soldier
power=1
toughness=2
[/card]
activated ability gained a new restriction "opponentturnonly"
variables will now be recalculated during the resolve of the major abilities to produce the most current number.
{x}:draw:x <----
new number variables words:
using draw as an exsample
draw:auras <--auras on a creature
draw:type:ally <---counts the allys in your field. self explanitory
draw:thatmuch <--mostly a triggered effects number.
when you take damage draw that much
draw:lifelost
draw:oplifelost
these return the value of the life lost that turn.
new TRIGGER restricitions
sourcenottap
sourceTap
foelostthree<--card cycle uses opponent lost life
foelosttwo<--same as above
once<--this trigger will only ever trigger one time and never again.
new card discriptor words
[multicolor]
[leveler]
[enchanted]
[blackandgreen]
[blackandwhite]
[redandblue]
[blueandgreen]
[redandwhite]
CD will now recalculate the number again on resolve
meaning {x}:target(CreatureTargetChooser[manacost <=x]) will work, with an added bonus {x}:target(CreatureTargetChooser[manacost <=any word variable])
new this(:
this(tapped)<--for strange case cards.
this(untapped)
this(auras)
new MTGAbility keywords
(blink)
(blink)forsrc <--stay blinked while source inplay
hand(blink <---adding hand to the front makes it target hand.
livingweapon
this is an extension of token, simple attach the words "livingweapon" to the front of token( and it will autoamtically token that and attach the card to it.
token( gained:
"targetcontroller" targetting.
"battleready" if put in the tokens abilities it will be a attacker and tapped as it is entering play.
phaseout <--self explanitory
spiritlink <--stacking lifelink style effect that benifits the OWNER of the card.
combatspiritlink same as above.
stacking flanking, requires 2 abilities unfortunately
[card]
name=Agility
target=creature
auto=teach(creature) flanker
auto=teach(creature) flanking
text=Enchant creature -- Enchanted creature gets +1/+1 and has flanking. (Whenever a creature without flanking blocks this creature, the blocking
creature gets -1/-1 until end of turn.)
mana={1}{R}
type=Enchantment
subtype=Aura
[/card]
removeallcounters(number/number,name)
removes all counters of the type from a card, can all be
"all"
vampire hexmage effect.
added new tools for transforms
,setpower=number
,settoughness=number
removetypes
morph
autofacedown={0}:morph
eradicate <---same as the card name.
cumulativeupcost[ <--self explanitory
upcostmulti[ <--an upcost that will resolve with a && ability
phaseaction[ phase name ] ability
an ability that will trigger on the stated phase name.
also support for phaseactionmulti[
new triggers added:
@vampired( <--sengir vampire effect
@targeted(
@lifeloss(
@lifed(
add a special ability builder called dynamicability
it acts alot like a choose your own adventure book
dynamicability<! variable 1, variable 2, variable 3,variable 4!> optional ability targetting the original target.
variable list 1:
this is the primary amount source
source
mytgt
myself
myfoe
variable list 2:
this is the variable we're after, or the amount
power
toughness
manacost
colors
age
charge
oneonecounters
thatmuch
variable list 3:
this is the main effect
strike
draw
lifeloss
lifegain
pumppow
pumptough
pumpboth
deplete
countersoneone
variable list 4:
how it will do this effect to.
itself
eachother
targetcontroller
targetopponent
tosrc
srccontroller
srcopponent
the best way to explain its usage is to look at cards coded with this ability. or experiment with combinations.
new gameoption
First turn player:player, opponent, random
who takes the first turn
added poisoned status, tho not complete since MBS hasnt spoiled enough cards to see where this variable will be used.
taught ai how to counter spell
improved ai, it will now cast instants during interupts and during your turn.
previously ai treated instant cards the same as it treated sorceries, which was not fair to the ai.
im sure there is some messed items, but the rev directly before this one had formatting in the code that created hundreds of conflicts with this one, so i had to dig this info out of red and green sections.
cards and test are coming soon, i ask PLEASE do not alter these new additions until the test are commited.
im commiting without the test because instead of allowing me to proceed with my beta test period, there are some that wish to rush me into a commit. if you do not like this commit revert it, i absolutely on no grounds give permission to recommit afterwards. and i will not recommit if a revert is called.
5877 lines
158 KiB
C++
5877 lines
158 KiB
C++
#ifndef _CARDS_H_
|
|
#define _CARDS_H_
|
|
|
|
#include "DebugRoutines.h"
|
|
#include "MTGAbility.h"
|
|
#include "ManaCost.h"
|
|
#include "CardDescriptor.h"
|
|
#include "AIPlayer.h"
|
|
#include "CardDisplay.h"
|
|
#include "Subtypes.h"
|
|
#include "CardGui.h"
|
|
#include "GameOptions.h"
|
|
#include "Token.h"
|
|
#include "Counters.h"
|
|
#include "WEvent.h"
|
|
#include "GuiStatic.h"
|
|
#include "GameObserver.h"
|
|
#include "ThisDescriptor.h"
|
|
|
|
#include <JGui.h>
|
|
#include <hge/hgeparticle.h>
|
|
|
|
#include <map>
|
|
using std::map;
|
|
|
|
//
|
|
// Misc classes
|
|
//
|
|
class WParsedInt
|
|
{
|
|
public:
|
|
int intValue;
|
|
|
|
int computeX(Spell * spell, MTGCardInstance * card)
|
|
{
|
|
if (spell) return spell->computeX(card);
|
|
if (card) return card->X;
|
|
return 1; //this should only hapen when the ai calls the ability. This is to give it an idea of the "direction" of X (positive/negative)
|
|
}
|
|
int computeXX(Spell * spell, MTGCardInstance * card)
|
|
{
|
|
if (spell) return spell->computeXX(card);
|
|
if (card) return card->XX;
|
|
return 1; //this should only hapen when the ai calls the ability. This is to give it an idea of the "direction" of X (positive/negative)
|
|
}
|
|
WParsedInt(int value = 0)
|
|
{
|
|
intValue = value;
|
|
}
|
|
|
|
WParsedInt(string s, Spell * spell, MTGCardInstance * card)
|
|
{
|
|
MTGCardInstance * target = card->target;
|
|
intValue = 0;
|
|
if (!target) target = card;
|
|
int multiplier = 1;
|
|
if (s[0] == '-')
|
|
{
|
|
s = s.substr(1);
|
|
multiplier = -1;
|
|
}
|
|
if (s == "x" || s == "X")
|
|
{
|
|
intValue = computeX(spell, card);
|
|
if(intValue < 0)
|
|
intValue = 0;
|
|
}
|
|
else if (s == "xx" || s == "XX")
|
|
{
|
|
intValue = computeXX(spell, card);
|
|
if(intValue < 0)
|
|
intValue = 0;
|
|
}
|
|
else if (s == "gear")
|
|
{
|
|
intValue = target->equipment;
|
|
}
|
|
else if (s == "auras")
|
|
{
|
|
intValue = target->auras;
|
|
}
|
|
else if (s == "manacost")
|
|
{
|
|
intValue = target->getManaCost()->getConvertedCost();
|
|
}
|
|
else if (s.find("type:") != string::npos)
|
|
{
|
|
size_t begins = s.find(":");
|
|
string theType = s.substr(begins + 1);
|
|
intValue = target->controller()->game->inPlay->countByType(theType.c_str());
|
|
}
|
|
else if (s == "sunburst")
|
|
{
|
|
intValue = 0;
|
|
if (card && card->previous && card->previous->previous)
|
|
{
|
|
intValue = card->previous->previous->sunburst;
|
|
}
|
|
}
|
|
else if (s == "lifetotal")
|
|
{
|
|
intValue = target->controller()->life;
|
|
}
|
|
else if (s == "thatmuch")
|
|
{
|
|
intValue = 0;
|
|
intValue = target->thatmuch;
|
|
int checkagain = 0;
|
|
if(target->hasSubtype("aura") || target->hasSubtype("equipment"))
|
|
{
|
|
if(target->target)
|
|
{
|
|
checkagain = target->target->thatmuch;
|
|
}
|
|
}
|
|
if(checkagain > intValue)
|
|
intValue = checkagain;
|
|
}
|
|
else if (s == "oplifelost")
|
|
{
|
|
intValue = target->controller()->opponent()->lifeLostThisTurn;
|
|
}
|
|
else if (s == "lifelost")
|
|
{
|
|
intValue = target->controller()->lifeLostThisTurn;
|
|
}
|
|
else if (s == "pdcount")
|
|
{
|
|
intValue = target->controller()->damageCount;
|
|
}
|
|
else if (s == "odcount")
|
|
{
|
|
intValue = target->controller()->opponent()->damageCount;
|
|
}
|
|
else if (s == "opponentlifetotal")
|
|
{
|
|
intValue = target->controller()->opponent()->life;
|
|
}
|
|
else if (s == "p" || s == "power")
|
|
{
|
|
intValue = target->getPower();
|
|
}
|
|
else if (s == "t" || s == "toughness")
|
|
{
|
|
intValue = target->getToughness();
|
|
}
|
|
else
|
|
{
|
|
intValue = atoi(s.c_str());
|
|
}
|
|
intValue *= multiplier;
|
|
}
|
|
|
|
int getValue()
|
|
{
|
|
return intValue;
|
|
}
|
|
};
|
|
|
|
class WParsedPT
|
|
{
|
|
public:
|
|
bool ok;
|
|
WParsedInt power, toughness;
|
|
|
|
WParsedPT(int p, int t)
|
|
{
|
|
power.intValue = p;
|
|
toughness.intValue = t;
|
|
ok = true;
|
|
}
|
|
|
|
WParsedPT(string s, Spell * spell, MTGCardInstance * card)
|
|
{
|
|
size_t found = s.find("/");
|
|
ok = false;
|
|
if (found != string::npos)
|
|
{
|
|
size_t end = s.find(" ", found);
|
|
if (end == string::npos) end = s.size();
|
|
size_t start = s.find_last_of(" ", found);
|
|
if (start == string::npos)
|
|
start = 0;
|
|
else
|
|
start++;
|
|
power = WParsedInt(s.substr(start, found - start), spell, card);
|
|
toughness = WParsedInt(s.substr(found + 1, end - found - 1), spell, card);
|
|
|
|
ok = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
//
|
|
//Triggers
|
|
//
|
|
|
|
|
|
// Triggers When a card gets added to a zone (@movedTo)
|
|
class TrCardAddedToZone: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetZoneChooser * toTcZone, *fromTcZone;
|
|
TargetChooser * toTcCard, *fromTcCard;
|
|
bool once;
|
|
bool sourceUntapped;
|
|
bool activeTrigger;
|
|
TrCardAddedToZone(int id, MTGCardInstance * source, TargetZoneChooser * toTcZone, TargetChooser * toTcCard,
|
|
TargetZoneChooser * fromTcZone = NULL, TargetChooser * fromTcCard = NULL,bool once = false,bool sourceUntapped = false) :
|
|
TriggeredAbility(id, source), toTcZone(toTcZone), fromTcZone(fromTcZone), toTcCard(toTcCard), fromTcCard(fromTcCard),once(once),sourceUntapped(sourceUntapped)
|
|
{
|
|
activeTrigger = true;
|
|
}
|
|
;
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventZoneChange * e = dynamic_cast<WEventZoneChange*> (event);
|
|
if (!e) return 0;
|
|
if(sourceUntapped == true && source->isTapped() == 1)
|
|
return 0;
|
|
if(activeTrigger == false)
|
|
return 0;
|
|
if (!toTcZone->targetsZone(e->to)) return 0;
|
|
if (!toTcCard->canTarget(e->card)) return 0;
|
|
if (fromTcZone && !fromTcZone->targetsZone(e->from)) return 0;
|
|
if (fromTcCard && !fromTcCard->canTarget(e->card->previous)) return 0;
|
|
|
|
//Battlefield is a special case. We usually don't want to trigger when a card comes from battlefield to battlefield
|
|
// http://code.google.com/p/wagic/issues/detail?id=179
|
|
if ((e->from == game->players[0]->game->battlefield || e->from == game->players[1]->game->battlefield) && (e->to
|
|
== game->players[0]->game->battlefield || e->to == game->players[1]->game->battlefield))
|
|
{
|
|
return 0;
|
|
}
|
|
if(once == true && activeTrigger == true)
|
|
activeTrigger = false;
|
|
return 1;
|
|
}
|
|
|
|
~TrCardAddedToZone()
|
|
{
|
|
SAFE_DELETE(toTcZone);
|
|
SAFE_DELETE(toTcCard);
|
|
SAFE_DELETE(fromTcZone);
|
|
SAFE_DELETE(fromTcCard);
|
|
}
|
|
|
|
TrCardAddedToZone * clone() const
|
|
{
|
|
TrCardAddedToZone * a = NEW TrCardAddedToZone(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrCardTapped: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
bool tap;
|
|
TrCardTapped(int id, MTGCardInstance * source, TargetChooser * tc, bool tap = true) :
|
|
TriggeredAbility(id, source), tc(tc), tap(tap)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventCardTap * e = dynamic_cast<WEventCardTap *> (event);
|
|
if (!e) return 0;
|
|
if (e->before == e->after) return 0;
|
|
if (e->after != tap) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
~TrCardTapped()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
}
|
|
|
|
TrCardTapped * clone() const
|
|
{
|
|
TrCardTapped * a = NEW TrCardTapped(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrCardTappedformana: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
bool tap;
|
|
TrCardTappedformana(int id, MTGCardInstance * source, TargetChooser * tc, bool tap = true) :
|
|
TriggeredAbility(id, source), tc(tc), tap(tap)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventCardTappedForMana * e = dynamic_cast<WEventCardTappedForMana *> (event);
|
|
if (!e) return 0;
|
|
if (e->before == e->after) return 0;
|
|
if (e->after != tap) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
~TrCardTappedformana()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
}
|
|
|
|
TrCardTappedformana * clone() const
|
|
{
|
|
TrCardTappedformana * a = NEW TrCardTappedformana(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrCardAttackedNotBlocked: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
TrCardAttackedNotBlocked(int id, MTGCardInstance * source, TargetChooser * tc) :
|
|
TriggeredAbility(id, source), tc(tc)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventCardAttackedNotBlocked * e = dynamic_cast<WEventCardAttackedNotBlocked *> (event);
|
|
if (!e) return 0;
|
|
if (e->card->didattacked < 1) return 0;
|
|
if (e->card->blocked) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
~TrCardAttackedNotBlocked()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
}
|
|
|
|
TrCardAttackedNotBlocked * clone() const
|
|
{
|
|
TrCardAttackedNotBlocked * a = NEW TrCardAttackedNotBlocked(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrCardAttackedBlocked: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
TargetChooser * fromTc;
|
|
TrCardAttackedBlocked(int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL) :
|
|
TriggeredAbility(id, source), tc(tc), fromTc(fromTc)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventCardAttackedBlocked * e = dynamic_cast<WEventCardAttackedBlocked *> (event);
|
|
if (!e) return 0;
|
|
if (e->card->didattacked < 1) return 0;
|
|
if (!e->card->blocked) return 0;
|
|
if (fromTc && !fromTc->canTarget(e->card->getNextOpponent())) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
~TrCardAttackedBlocked()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
TrCardAttackedBlocked * clone() const
|
|
{
|
|
TrCardAttackedBlocked * a = NEW TrCardAttackedBlocked(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrCardAttacked: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
bool sourceUntapped;
|
|
TrCardAttacked(int id, MTGCardInstance * source, TargetChooser * tc,bool sourceUntapped) :
|
|
TriggeredAbility(id, source), tc(tc), sourceUntapped(sourceUntapped)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventCardAttacked * e = dynamic_cast<WEventCardAttacked *> (event);
|
|
if (!e) return 0;
|
|
if (sourceUntapped == true && source->isTapped() == 1)
|
|
return 0;
|
|
if (e->card->didattacked < 1) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
~TrCardAttacked()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
}
|
|
|
|
TrCardAttacked * clone() const
|
|
{
|
|
TrCardAttacked * a = NEW TrCardAttacked(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrCardAttackedAlone: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
TrCardAttackedAlone(int id, MTGCardInstance * source, TargetChooser * tc) :
|
|
TriggeredAbility(id, source), tc(tc)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventCardAttackedAlone * e = dynamic_cast<WEventCardAttackedAlone *> (event);
|
|
if (!e) return 0;
|
|
if (e->card->didattacked < 1) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
~TrCardAttackedAlone()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
}
|
|
|
|
TrCardAttackedAlone * clone() const
|
|
{
|
|
TrCardAttackedAlone * a = NEW TrCardAttackedAlone(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrCardBlocked: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
TargetChooser * fromTc;
|
|
bool once;
|
|
bool activeTrigger;
|
|
TrCardBlocked(int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL,bool once = false) :
|
|
TriggeredAbility(id, source), tc(tc), fromTc(fromTc), once(once)
|
|
{
|
|
activeTrigger = true;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventCardBlocked * e = dynamic_cast<WEventCardBlocked *> (event);
|
|
if (!e) return 0;
|
|
if(activeTrigger == false)
|
|
return 0;
|
|
//if(e->card->didblocked < 1) return 0;
|
|
if (fromTc && !fromTc->canTarget(e->card->getNextOpponent())) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
if(once == true && activeTrigger == true)
|
|
activeTrigger = false;
|
|
return 1;
|
|
}
|
|
|
|
~TrCardBlocked()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
TrCardBlocked * clone() const
|
|
{
|
|
TrCardBlocked * a = NEW TrCardBlocked(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrcardDrawn: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
TrcardDrawn(int id, MTGCardInstance * source, TargetChooser * tc) :
|
|
TriggeredAbility(id, source), tc(tc)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventcardDraw * e = dynamic_cast<WEventcardDraw *> (event);
|
|
if (!e) return 0;
|
|
if (!tc->canTarget(e->player)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
~TrcardDrawn()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
}
|
|
|
|
TrcardDrawn * clone() const
|
|
{
|
|
TrcardDrawn * a = NEW TrcardDrawn(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrCardSacrificed: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
TrCardSacrificed(int id, MTGCardInstance * source, TargetChooser * tc) :
|
|
TriggeredAbility(id, source), tc(tc)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventCardSacrifice * e = dynamic_cast<WEventCardSacrifice *> (event);
|
|
if (!e) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
return 1;
|
|
}
|
|
|
|
~TrCardSacrificed()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
}
|
|
|
|
TrCardSacrificed * clone() const
|
|
{
|
|
TrCardSacrificed * a = NEW TrCardSacrificed(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrCardDiscarded: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
TrCardDiscarded(int id, MTGCardInstance * source, TargetChooser * tc) :
|
|
TriggeredAbility(id, source), tc(tc)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventCardDiscard * e = dynamic_cast<WEventCardDiscard *> (event);
|
|
if (!e) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
return 1;
|
|
}
|
|
~TrCardDiscarded()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
}
|
|
TrCardDiscarded * clone() const
|
|
{
|
|
TrCardDiscarded * a = NEW TrCardDiscarded(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrDamaged: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
TargetChooser * fromTc;
|
|
int type;//this allows damagenoncombat and combatdamage to share this trigger
|
|
bool sourceUntapped;
|
|
TrDamaged(int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL, int type = 0,bool sourceUntapped = false) :
|
|
TriggeredAbility(id, source), tc(tc), fromTc(fromTc), type(type) , sourceUntapped(sourceUntapped)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventDamage * e = dynamic_cast<WEventDamage *> (event);
|
|
if (!e) return 0;
|
|
if (sourceUntapped == true && source->isTapped() == 1)
|
|
return 0;
|
|
if (!tc->canTarget(e->damage->target)) return 0;
|
|
if (fromTc && !fromTc->canTarget(e->damage->source)) return 0;
|
|
if (type == 1 && e->damage->typeOfDamage != DAMAGE_COMBAT) return 0;
|
|
if (type == 2 && e->damage->typeOfDamage == DAMAGE_COMBAT) return 0;
|
|
e->damage->target->thatmuch = e->damage->damage;
|
|
e->damage->source->thatmuch = e->damage->damage;
|
|
return 1;
|
|
}
|
|
|
|
~TrDamaged()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
TrDamaged * clone() const
|
|
{
|
|
TrDamaged * a = NEW TrDamaged(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class TrLifeGained: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
TargetChooser * fromTc;
|
|
int type;//this allows damagenoncombat and combatdamage to share this trigger
|
|
bool sourceUntapped;
|
|
TrLifeGained(int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL, int type = 0,bool sourceUntapped = false) :
|
|
TriggeredAbility(id, source), tc(tc), fromTc(fromTc), type(type) , sourceUntapped(sourceUntapped)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventLife * e = dynamic_cast<WEventLife *> (event);
|
|
if (!e) return 0;
|
|
if (sourceUntapped == true && source->isTapped() == 1)
|
|
return 0;
|
|
if (!tc->canTarget(e->player)) return 0;
|
|
if (fromTc && !fromTc->canTarget(e->player)) return 0;
|
|
if (type == 1 && (e->amount > 0 || e->Ltype == 0)) return 0;
|
|
if (type == 0 && (e->amount < 0 || e->Ltype == 1)) return 0;
|
|
if(type != 1)
|
|
e->player->thatmuch = e->amount;
|
|
else
|
|
e->player->thatmuch = abs(e->amount);
|
|
return 1;
|
|
}
|
|
|
|
~TrLifeGained()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
TrLifeGained * clone() const
|
|
{
|
|
TrLifeGained * a = NEW TrLifeGained(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//vampire trigger
|
|
class TrVampired: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
TargetChooser * fromTc;
|
|
vector<MTGCardInstance*> victems;
|
|
int type;
|
|
TrVampired(int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL, int type = 0) :
|
|
TriggeredAbility(id, source), tc(tc), fromTc(fromTc), type(type)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventDamage * e = dynamic_cast<WEventDamage *> (event);
|
|
WEventZoneChange * z = dynamic_cast<WEventZoneChange *> (event);
|
|
WEventPhaseChange * pe = dynamic_cast<WEventPhaseChange*>(event);
|
|
WEventVampire * vamp = dynamic_cast<WEventVampire*>(event);
|
|
if (e == event)
|
|
{
|
|
if (!tc->canTarget(e->damage->target)) return 0;
|
|
if (fromTc && !fromTc->canTarget(e->damage->source)) return 0;
|
|
|
|
MTGCardInstance * newVictem = (MTGCardInstance*)(e->damage->target);
|
|
|
|
victems.push_back(newVictem);
|
|
std::sort(victems.begin(), victems.end());
|
|
victems.erase(std::unique(victems.begin(), victems.end()), victems.end());
|
|
}
|
|
else if (z == event && !victems.empty())
|
|
{
|
|
MTGCardInstance * card = z->card;
|
|
|
|
|
|
for(unsigned int k = 0;k < victems.size();k--)
|
|
{
|
|
if(victems[k] == NULL)
|
|
continue;
|
|
if(victems[k] != z->card->previous)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
for(unsigned int w = 0;w < victems.size();w++)
|
|
{
|
|
if(victems[w] == NULL)
|
|
continue;
|
|
Player * p = victems[w]->controller();
|
|
if (z->from == p->game->inPlay && z->to == p->game->graveyard)
|
|
{
|
|
if(victems[w] == z->card->previous)
|
|
{
|
|
if(!source->isInPlay())
|
|
return 0;
|
|
WEvent * e = NEW WEventVampire(victems[w],victems[w],source);
|
|
game->receiveEvent(e);
|
|
victems[w] = NULL;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (vamp == event)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (pe == event)
|
|
{
|
|
if( pe->from->id == Constants::MTG_PHASE_ENDOFTURN)
|
|
{
|
|
victems.clear();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
~TrVampired()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
TrVampired * clone() const
|
|
{
|
|
TrVampired * a = NEW TrVampired(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//targetted trigger
|
|
class TrTargeted: public TriggeredAbility
|
|
{
|
|
public:
|
|
TargetChooser * tc;
|
|
TargetChooser * fromTc;
|
|
int type;
|
|
TrTargeted(int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL, int type = 0) :
|
|
TriggeredAbility(id, source), tc(tc), fromTc(fromTc), type(type)
|
|
{
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 0; //This is a trigger, this function should not be called
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if(source->isPhased) return 0;
|
|
WEventTarget * e = dynamic_cast<WEventTarget *> (event);
|
|
if (!e) return 0;
|
|
if (!tc->canTarget(e->card)) return 0;
|
|
if (fromTc && !fromTc->canTarget(e->source)) return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
~TrTargeted()
|
|
{
|
|
SAFE_DELETE(tc);
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
TrTargeted * clone() const
|
|
{
|
|
TrTargeted * a = NEW TrTargeted(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
//counters
|
|
class AACounter: public ActivatedAbility
|
|
{
|
|
public:
|
|
string counterstring;
|
|
int nb;
|
|
int power;
|
|
int toughness;
|
|
string name;
|
|
string menu;
|
|
|
|
AACounter(int id, MTGCardInstance * source, MTGCardInstance * target,string counterstring, const char * _name, int power, int toughness, int nb,
|
|
ManaCost * cost = NULL, int doTap = 0);
|
|
|
|
int resolve();
|
|
const char* getMenuText();
|
|
AACounter * clone() const;
|
|
};
|
|
|
|
//counters
|
|
class AARemoveAllCounter: public ActivatedAbility
|
|
{
|
|
public:
|
|
int nb;
|
|
int power;
|
|
int toughness;
|
|
string name;
|
|
string menu;
|
|
bool all;
|
|
|
|
AARemoveAllCounter(int id, MTGCardInstance * source, MTGCardInstance * target, const char * _name, int power, int toughness, int nb,
|
|
bool all,ManaCost * cost = NULL, int doTap = 0);
|
|
|
|
int resolve();
|
|
const char* getMenuText();
|
|
AARemoveAllCounter * clone() const;
|
|
};
|
|
|
|
|
|
class AAFizzler: public ActivatedAbility
|
|
{
|
|
|
|
public:
|
|
AAFizzler(int _id, MTGCardInstance * card, Spell * _target, ManaCost * _cost = NULL, int _tap = 0);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AAFizzler* clone() const;
|
|
};
|
|
|
|
/*
|
|
Generic classes
|
|
*/
|
|
|
|
//MayAbility: May do ...
|
|
class MayAbility: public MTGAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int triggered;
|
|
bool must;
|
|
MTGAbility * mClone;
|
|
|
|
MayAbility(int _id, MTGAbility * _ability, MTGCardInstance * _source, bool must = false);
|
|
|
|
void Update(float dt);
|
|
|
|
const char * getMenuText();
|
|
int testDestroy();
|
|
|
|
int isReactingToTargetClick(Targetable * card);
|
|
|
|
int reactToTargetClick(Targetable * object);
|
|
|
|
MayAbility * clone() const;
|
|
~MayAbility();
|
|
|
|
};
|
|
|
|
//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);
|
|
int Add(MTGAbility * ability);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
MultiAbility * clone() const;
|
|
~MultiAbility();
|
|
};
|
|
|
|
//Generic Activated Ability
|
|
|
|
class GenericActivatedAbility: public ActivatedAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int limitPerTurn;
|
|
int counters;
|
|
MTGGameZone * activeZone;
|
|
|
|
GenericActivatedAbility(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int _tap = 0, int limit = 0,
|
|
int restrictions = 0, MTGGameZone * dest = NULL);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL);
|
|
void Update(float dt);
|
|
int testDestroy();
|
|
GenericActivatedAbility * clone() const;
|
|
~GenericActivatedAbility();
|
|
|
|
};
|
|
|
|
//Copier. ActivatedAbility
|
|
class AACopier: public ActivatedAbility
|
|
{
|
|
public:
|
|
AACopier(int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AACopier * clone() const;
|
|
};
|
|
//imprint
|
|
class AAPhaseOut: public ActivatedAbility
|
|
{
|
|
public:
|
|
AAPhaseOut(int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost = NULL);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AAPhaseOut * clone() const;
|
|
};
|
|
//cloning...this makes a token thats a copy of the target.
|
|
class AACloner: public ActivatedAbility
|
|
{
|
|
public:
|
|
int who;
|
|
string with;
|
|
list<int> awith;
|
|
list<int> colors;
|
|
|
|
AACloner(int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost = NULL, int who = 0,
|
|
string abilitiesStringList = "");
|
|
int resolve();
|
|
const char * getMenuText();
|
|
virtual ostream& toString(ostream& out) const;
|
|
AACloner * clone() const;
|
|
~AACloner();
|
|
};
|
|
|
|
// AAMover
|
|
class AAMover: public ActivatedAbility
|
|
{
|
|
public:
|
|
string destination;
|
|
|
|
AAMover(int _id, MTGCardInstance * _source, MTGCardInstance * _target, string dest, ManaCost * _cost = NULL, int doTap = 0);
|
|
MTGGameZone * destinationZone();
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AAMover * clone() const;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------------------------
|
|
class AABanishCard: public ActivatedAbility
|
|
{
|
|
|
|
protected:
|
|
|
|
public:
|
|
int banishmentType;
|
|
const static int BANISHED = -1;
|
|
const static int BURY = 0;
|
|
const static int DESTROY = 1;
|
|
const static int SACRIFICE = 2;
|
|
const static int DISCARD = 3;
|
|
|
|
AABanishCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _banishmentType = BANISHED);
|
|
int resolve();
|
|
virtual const char * getMenuText();
|
|
AABanishCard * clone() const;
|
|
};
|
|
|
|
class AABuryCard: public AABanishCard
|
|
{
|
|
public:
|
|
AABuryCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _banishmentType = BURY);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AABuryCard * clone() const;
|
|
};
|
|
|
|
class AADestroyCard: public AABanishCard
|
|
{
|
|
public:
|
|
AADestroyCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _banishmentType = DESTROY);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AADestroyCard * clone() const;
|
|
};
|
|
|
|
class AASacrificeCard: public AABanishCard
|
|
{
|
|
public:
|
|
AASacrificeCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _banishmentType = SACRIFICE);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AASacrificeCard * clone() const;
|
|
};
|
|
|
|
class AADiscardCard: public AABanishCard
|
|
{
|
|
public:
|
|
AADiscardCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _banishmentType = DISCARD);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AADiscardCard * clone() const;
|
|
};
|
|
|
|
/* Generic Target Ability */
|
|
class GenericTargetAbility: public TargetAbility
|
|
{
|
|
|
|
public:
|
|
int limitPerTurn;
|
|
int counters;
|
|
MTGGameZone * activeZone;
|
|
|
|
GenericTargetAbility(int _id, MTGCardInstance * _source, TargetChooser * _tc, MTGAbility * a, ManaCost * _cost = NULL,
|
|
int _tap = 0, int limit = 0, int restrictions = 0, MTGGameZone * dest = NULL);
|
|
const char * getMenuText();
|
|
~GenericTargetAbility();
|
|
GenericTargetAbility * clone() const;
|
|
int resolve();
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL);
|
|
void Update(float dt);
|
|
int testDestroy();
|
|
|
|
};
|
|
|
|
//Cycling
|
|
|
|
class ACycle: public ActivatedAbility
|
|
{
|
|
public:
|
|
ACycle(int _id, MTGCardInstance * card, Targetable * _target) :
|
|
ActivatedAbility(_id, card)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
WEvent * e = NEW WEventCardDiscard(source);
|
|
GameObserver * game = GameObserver::GetInstance();
|
|
game->receiveEvent(e);
|
|
source->controller()->game->putInGraveyard(source);
|
|
source->controller()->game->drawFromLibrary();
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return "Cycling";
|
|
}
|
|
|
|
ACycle * clone() const
|
|
{
|
|
ACycle * a = NEW ACycle(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
};
|
|
|
|
//ninjutsu
|
|
|
|
class ANinja: public ActivatedAbility
|
|
{
|
|
public:
|
|
ANinja(int _id, MTGCardInstance * card, Targetable * _target) :
|
|
ActivatedAbility(_id, card)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * copy = source->controller()->game->putInZone(source, source->controller()->game->hand,
|
|
source->controller()->game->temp);
|
|
Spell * spell = NEW Spell(copy);
|
|
spell->resolve();
|
|
MTGCardInstance * newcard = spell->source;
|
|
newcard->summoningSickness = 0;
|
|
newcard->tap();
|
|
newcard->setAttacker(1);
|
|
delete spell;
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return "Ninjutsu";
|
|
}
|
|
|
|
ANinja * clone() const
|
|
{
|
|
ANinja * a = NEW ANinja(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//remove from combat
|
|
|
|
class ACombatRemovel: public ActivatedAbility
|
|
{
|
|
public:
|
|
ACombatRemovel(int _id, MTGCardInstance * card, Targetable * _target) :
|
|
ActivatedAbility(_id, card)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
_target->initAttackersDefensers();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return "Remove From Combat";
|
|
}
|
|
|
|
ACombatRemovel * clone() const
|
|
{
|
|
ACombatRemovel * a = NEW ACombatRemovel(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Drawer, allows to draw a card for a cost:
|
|
|
|
class AADrawer: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
WParsedInt *nbcards;
|
|
WParsedInt *RefreshedNbcards;
|
|
|
|
string nbcardsStr;
|
|
|
|
AADrawer(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, WParsedInt * nbcards,string nbcardsStr, int _tap = 0, int who =
|
|
TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AADrawer * clone() const;
|
|
~AADrawer();
|
|
};
|
|
|
|
//lands, allows to play more land during a turn:
|
|
|
|
class AAMoreLandPlz: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
WParsedInt *additional;
|
|
|
|
AAMoreLandPlz(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost, WParsedInt * _additional, int _tap = 0,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AAMoreLandPlz * clone() const;
|
|
~AAMoreLandPlz();
|
|
|
|
};
|
|
|
|
/*Gives life to target controller*/
|
|
class AALifer: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
string life_s;
|
|
WParsedInt *life;
|
|
WParsedInt *RefreshedLife;
|
|
AALifer(int _id, MTGCardInstance * card, Targetable * _target,string life_s, WParsedInt * life, ManaCost * _cost = NULL, int _tap = 0,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AALifer * clone() const;
|
|
~AALifer();
|
|
|
|
};
|
|
|
|
/*Player Wins Game*/
|
|
class AAWinGame: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
AAWinGame(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int _tap = 0, int who =
|
|
TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AAWinGame * clone() const;
|
|
};
|
|
|
|
class ATokenCreator: public ActivatedAbility
|
|
{
|
|
public:
|
|
list<int> abilities;
|
|
list<int> types;
|
|
list<int> colors;
|
|
int power, toughness;
|
|
int tokenId;
|
|
string name;
|
|
string sabilities;
|
|
string starfound;
|
|
WParsedInt * multiplier;
|
|
int who;
|
|
bool aLivingWeapon;
|
|
bool battleReady;
|
|
MTGCardInstance * myToken;
|
|
vector<MTGAbility *> currentAbilities;
|
|
ATokenCreator(int _id, MTGCardInstance * _source, Targetable * _target, ManaCost * _cost, int tokenId, int _doTap,string starfound, WParsedInt * multiplier = NULL,
|
|
int who = 0,bool aLivingWeapon = false) :
|
|
ActivatedAbility(_id, _source, _cost, 0, _doTap), tokenId(tokenId), starfound(starfound),multiplier(multiplier), who(who),aLivingWeapon(aLivingWeapon)
|
|
{
|
|
if (!multiplier) this->multiplier = NEW WParsedInt(1);
|
|
MTGCard * card = GameApp::collection->getCardById(tokenId);
|
|
if (card) name = card->data->getName();
|
|
battleReady = false;
|
|
}
|
|
|
|
ATokenCreator(int _id, MTGCardInstance * _source, Targetable * _target, ManaCost * _cost, string sname, string stypes, int _power, int _toughness,
|
|
string sabilities, int _doTap, string starfound,WParsedInt * multiplier = NULL, int who = 0,bool aLivingWeapon = false) :
|
|
ActivatedAbility(_id, _source, _cost, 0, _doTap),sabilities(sabilities),starfound(starfound), multiplier(multiplier), who(who),aLivingWeapon(aLivingWeapon)
|
|
{
|
|
power = _power;
|
|
toughness = _toughness;
|
|
name = sname;
|
|
who = who;
|
|
tokenId = 0;
|
|
aType = MTGAbility::STANDARD_TOKENCREATOR;
|
|
battleReady = false;
|
|
if (!multiplier) this->multiplier = NEW WParsedInt(1);
|
|
//TODO this is a copy/past of other code that's all around the place, everything should be in a dedicated parser class;
|
|
|
|
for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++)
|
|
{
|
|
size_t found = sabilities.find(Constants::MTGBasicAbilities[j]);
|
|
if (found != string::npos)
|
|
{
|
|
abilities.push_back(j);
|
|
}
|
|
}
|
|
|
|
if(sabilities.find("battleready") != string::npos)
|
|
battleReady = true;
|
|
|
|
for (int j = 0; j < Constants::MTG_NB_COLORS; j++)
|
|
{
|
|
size_t found = sabilities.find(Constants::MTGColorStrings[j]);
|
|
if (found != string::npos)
|
|
{
|
|
colors.push_back(j);
|
|
}
|
|
}
|
|
|
|
string s = stypes;
|
|
while (s.size())
|
|
{
|
|
size_t found = s.find(" ");
|
|
if (found != string::npos)
|
|
{
|
|
int id = Subtypes::subtypesList->find(s.substr(0, found));
|
|
types.push_back(id);
|
|
s = s.substr(found + 1);
|
|
}
|
|
else
|
|
{
|
|
int id = Subtypes::subtypesList->find(s);
|
|
types.push_back(id);
|
|
s = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
if(!starfound.empty())
|
|
{
|
|
multiplier = NEW WParsedInt(starfound, NULL, (MTGCardInstance *)source);
|
|
}
|
|
for (int i = 0; i < multiplier->getValue(); ++i)
|
|
{
|
|
//MTGCardInstance * myToken;
|
|
if (tokenId)
|
|
{
|
|
MTGCard * card = GameApp::collection->getCardById(tokenId);
|
|
myToken = NEW MTGCardInstance(card, source->controller()->game);
|
|
|
|
}
|
|
else
|
|
{
|
|
myToken = NEW Token(name, source, power, toughness);
|
|
list<int>::iterator it;
|
|
for (it = types.begin(); it != types.end(); it++)
|
|
{
|
|
myToken->addType(*it);
|
|
}
|
|
for (it = colors.begin(); it != colors.end(); it++)
|
|
{
|
|
myToken->setColor(*it);
|
|
}
|
|
for (it = abilities.begin(); it != abilities.end(); it++)
|
|
{
|
|
myToken->basicAbilities[*it] = 1;
|
|
}
|
|
}
|
|
string tokenText = "";
|
|
if(sabilities.find("token(") == string::npos)
|
|
{
|
|
tokenText = "(";
|
|
size_t endAbility = sabilities.find(")");
|
|
string words = sabilities.substr(0,endAbility);
|
|
tokenText.append(words);
|
|
string sourcename = ((MTGCardInstance*)source)->name;
|
|
tokenText.append(") source: ");
|
|
tokenText.append( sourcename);
|
|
myToken->text = tokenText;
|
|
}
|
|
if (who == 0 && who != 1 && who != 2)
|
|
{
|
|
source->controller()->game->temp->addCard(myToken);
|
|
Spell * spell = NEW Spell(myToken);
|
|
spell->resolve();
|
|
spell->source->isToken = 1;
|
|
spell->source->fresh = 1;
|
|
if(aLivingWeapon)
|
|
{
|
|
source->target = spell->source;
|
|
source->target->equipment += 1;
|
|
AbilityFactory af;
|
|
af.getAbilities(¤tAbilities, NULL, source);
|
|
for (size_t i = 0; i < currentAbilities.size(); ++i)
|
|
{
|
|
MTGAbility * a = currentAbilities[i];
|
|
if (a->aType == MTGAbility::STANDARD_EQUIP) continue;
|
|
if (a->aType == MTGAbility::STANDARD_TEACH) continue;
|
|
a->addToGame();
|
|
}
|
|
}
|
|
if(battleReady)
|
|
{
|
|
spell->source->summoningSickness = 0;
|
|
spell->source->tap();
|
|
spell->source->setAttacker(1);
|
|
}
|
|
delete spell;
|
|
}
|
|
else if (who == 1 && who != 0 && who != 2)
|
|
{
|
|
source->controller()->opponent()->game->temp->addCard(myToken);
|
|
Spell * spell = NEW Spell(myToken);
|
|
spell->resolve();
|
|
spell->source->owner = spell->source->controller();
|
|
spell->source->isToken = 1;
|
|
spell->source->fresh = 1;
|
|
if(aLivingWeapon)
|
|
{
|
|
source->target = spell->source;
|
|
source->target->equipment += 1;
|
|
AbilityFactory af;
|
|
af.getAbilities(¤tAbilities, NULL, source);
|
|
for (size_t i = 0; i < currentAbilities.size(); ++i)
|
|
{
|
|
MTGAbility * a = currentAbilities[i];
|
|
if (a->aType == MTGAbility::STANDARD_EQUIP) continue;
|
|
if (a->aType == MTGAbility::STANDARD_TEACH) continue;
|
|
a->addToGame();
|
|
}
|
|
}
|
|
if(battleReady)
|
|
{
|
|
spell->source->summoningSickness = 0;
|
|
spell->source->tap();
|
|
spell->source->setAttacker(1);
|
|
}
|
|
delete spell;
|
|
}
|
|
else
|
|
{
|
|
((MTGCardInstance*)target)->controller()->game->temp->addCard(myToken);
|
|
Spell * spell = NEW Spell(myToken);
|
|
spell->resolve();
|
|
spell->source->owner = ((MTGCardInstance*)target)->controller();
|
|
spell->source->isToken = 1;
|
|
spell->source->fresh = 1;
|
|
myToken = spell->source;
|
|
if(aLivingWeapon)
|
|
{
|
|
source->target = spell->source;
|
|
source->target->equipment += 1;
|
|
AbilityFactory af;
|
|
af.getAbilities(¤tAbilities, NULL, source);
|
|
for (size_t i = 0; i < currentAbilities.size(); ++i)
|
|
{
|
|
MTGAbility * a = currentAbilities[i];
|
|
if (a->aType == MTGAbility::STANDARD_EQUIP) continue;
|
|
if (a->aType == MTGAbility::STANDARD_TEACH) continue;
|
|
a->addToGame();
|
|
}
|
|
}
|
|
if(battleReady)
|
|
{
|
|
spell->source->summoningSickness = 0;
|
|
spell->source->tap();
|
|
spell->source->setAttacker(1);
|
|
}
|
|
delete spell;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
sprintf(menuText, "Create %s", name.c_str());
|
|
return menuText;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ATokenCreator ::: abilities : ?" // << abilities
|
|
<< " ; types : ?" // << types
|
|
<< " ; colors : ?" // << colors
|
|
<< " ; power : " << power << " ; toughness : " << toughness << " ; name : " << name << " ; who : " << who << " (";
|
|
return ActivatedAbility::toString(out) << ")";
|
|
}
|
|
|
|
ATokenCreator * clone() const
|
|
{
|
|
ATokenCreator * a = NEW ATokenCreator(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
~ATokenCreator()
|
|
{
|
|
if (!isClone)
|
|
{
|
|
if(multiplier != NULL)
|
|
delete (multiplier);
|
|
}
|
|
}
|
|
|
|
};
|
|
//naming an ability line-------------------------------------------------------------------------
|
|
class ANamer: public ActivatedAbility
|
|
{
|
|
public:
|
|
string name;
|
|
ANamer(int _id, MTGCardInstance * _source, ManaCost * _cost, string sname, int _doTap) :
|
|
ActivatedAbility(_id, _source, _cost, 0, _doTap)
|
|
{
|
|
name = sname;
|
|
}
|
|
int resolve()
|
|
{
|
|
return 0;
|
|
}
|
|
const char * getMenuText()
|
|
{
|
|
sprintf(menuText, "%s", name.c_str());
|
|
return menuText;
|
|
}
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ANamer ::: name" << name << " (";
|
|
return ActivatedAbility::toString(out) << ")";
|
|
}
|
|
ANamer * clone() const
|
|
{
|
|
ANamer * a = NEW ANamer(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
~ANamer()
|
|
{
|
|
if (!isClone)
|
|
{
|
|
}
|
|
}
|
|
};
|
|
|
|
/*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)
|
|
{
|
|
aType = MTGAbility::STANDARDABILITYGRANT;
|
|
abilitygranted = ability;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
value_before_modification = ((MTGCardInstance *) target)->basicAbilities[ability];
|
|
if(ability != Constants::ABSORB && ability != Constants::FLANKING)
|
|
{
|
|
((MTGCardInstance *) target)->basicAbilities[ability] = modifier;
|
|
}
|
|
else
|
|
{
|
|
((MTGCardInstance *) target)->basicAbilities[ability] += modifier;
|
|
}
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
if (((MTGCardInstance *) target)->basicAbilities[ability] == modifier && (ability != Constants::ABSORB && ability != Constants::FLANKING))
|
|
{
|
|
((MTGCardInstance *) target)->basicAbilities[ability] = value_before_modification;
|
|
return 1;
|
|
}
|
|
else if (ability == Constants::ABSORB || ability == Constants::FLANKING)
|
|
{
|
|
((MTGCardInstance *) target)->basicAbilities[ability] -= 1;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
//BUG !!!
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return Constants::MTGBasicAbilities[ability];
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ABasicAbilityModifier ::: modifier : " << modifier << " ; ability : " << ability
|
|
<< " ; value_before_modification : " << value_before_modification << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
|
|
ABasicAbilityModifier * clone() const
|
|
{
|
|
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)
|
|
{
|
|
aType = MTGAbility::STANDARDABILITYGRANT;
|
|
abilitygranted = ability;
|
|
nbTargets = 0;
|
|
tc = _tc;
|
|
if (!tc) tc = NEW CreatureTargetChooser(_source);
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UNTAP)
|
|
{
|
|
for (int i = 0; i < nbTargets; i++)
|
|
{
|
|
MTGCardInstance * mTarget = mTargets[i];
|
|
if (mTarget && mTarget->basicAbilities[ability])
|
|
{
|
|
mTarget->basicAbilities[ability] = stateBeforeActivation[i];
|
|
}
|
|
}
|
|
nbTargets = 0;
|
|
}
|
|
TargetAbility::Update(dt);
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * mTarget = tc->getNextCardTarget();
|
|
if (mTarget)
|
|
{
|
|
mTargets[nbTargets] = mTarget;
|
|
stateBeforeActivation[nbTargets] = mTarget->basicAbilities[ability];
|
|
mTarget->basicAbilities[ability] = modifier;
|
|
nbTargets++;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
resolve();
|
|
return ActivatedAbility::addToGame();
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return Constants::MTGBasicAbilities[ability];
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ABasicAbilityModifierUntilEOT ::: mTargets : " << mTargets << " ; nbTargets : " << nbTargets << " ; modifier : "
|
|
<< modifier << " ; stateBeforeActivation : " << stateBeforeActivation << " ; ability : " << ability << " (";
|
|
return TargetAbility::toString(out) << ")";
|
|
}
|
|
|
|
ABasicAbilityModifierUntilEOT * clone() const
|
|
{
|
|
ABasicAbilityModifierUntilEOT * a = NEW ABasicAbilityModifierUntilEOT(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
};
|
|
|
|
/*Instants that modifies a basic ability until end of turn */
|
|
class AInstantBasicAbilityModifierUntilEOT: public InstantAbility
|
|
{
|
|
public:
|
|
int stateBeforeActivation;
|
|
int ability;
|
|
int value;
|
|
AInstantBasicAbilityModifierUntilEOT(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _ability, int value) :
|
|
InstantAbility(_id, _source, _target), ability(_ability), value(value)
|
|
{
|
|
aType = MTGAbility::STANDARDABILITYGRANT;
|
|
abilitygranted = ability;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
stateBeforeActivation = _target->basicAbilities[ability];
|
|
if(ability != Constants::ABSORB)
|
|
{
|
|
_target->basicAbilities[ability] = value;
|
|
}
|
|
else
|
|
{
|
|
_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);
|
|
aType = MTGAbility::STANDARDABILITYGRANT;
|
|
abilitygranted = _ability;
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * cost = NULL)
|
|
{
|
|
//The upper level "GenericTargetAbility" takes care of the click so we always return 0 here
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGAbility * a = ability->clone();
|
|
a->target = target;
|
|
a->addToGame();
|
|
return 1;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
resolve();
|
|
return ActivatedAbility::addToGame();
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
ABasicAbilityAuraModifierUntilEOT * clone() const
|
|
{
|
|
ABasicAbilityAuraModifierUntilEOT * a = NEW ABasicAbilityAuraModifierUntilEOT(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
~ABasicAbilityAuraModifierUntilEOT()
|
|
{
|
|
if (!isClone)
|
|
SAFE_DELETE(ability);
|
|
}
|
|
};
|
|
|
|
/*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), lastUsedOn(NULL), lastChecked(NULL)
|
|
{
|
|
aType = MTGAbility::LIFER;
|
|
}
|
|
ASpellCastLife(int id, MTGCardInstance * _source, int color, ManaCost * _cost, int _life) :
|
|
MTGAbility(id, _source), cost(_cost), life(_life), lastUsedOn(NULL), lastChecked(NULL)
|
|
{
|
|
trigger.setColor(color);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL)
|
|
{
|
|
if (_card == source && game->currentlyActing()->game->inPlay->hasCard(source))
|
|
{
|
|
if (game->currentlyActing()->getManaPool()->canAfford(cost))
|
|
{
|
|
Interruptible * laststackitem = game->mLayers->stackLayer()->getAt(-1);
|
|
if (laststackitem && laststackitem->type == ACTION_SPELL)
|
|
{
|
|
Spell * spell = (Spell*) laststackitem;
|
|
if (spell->source != lastUsedOn && trigger.match(spell->source))
|
|
{
|
|
lastChecked = spell->source;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card)
|
|
{
|
|
if (!isReactingToClick(_card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(cost);
|
|
game->currentlyActing()->life += life;
|
|
lastUsedOn = lastChecked;
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ASpellCastLife ::: trigger : ? " // << trigger
|
|
<< " ; cost : " << cost << " ; life : " << life << " ; lastUsedOn : " << lastUsedOn << " ; lastChecked : "
|
|
<< lastChecked << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
|
|
ASpellCastLife * clone() const
|
|
{
|
|
ASpellCastLife * a = NEW ASpellCastLife(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
~ASpellCastLife()
|
|
{
|
|
SAFE_DELETE(cost);
|
|
}
|
|
|
|
};
|
|
|
|
//Allows to untap at any moment for an amount of mana
|
|
class AUnBlocker: public MTGAbility
|
|
{
|
|
public:
|
|
ManaCost * cost;
|
|
AUnBlocker(int id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) :
|
|
MTGAbility(id, _source, _target), cost(_cost)
|
|
{
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL)
|
|
{
|
|
if (_card == target && game->currentlyActing()->game->inPlay->hasCard(source) && (MTGCardInstance *) _card->isTapped())
|
|
{
|
|
if (game->currentlyActing()->getManaPool()->canAfford(cost))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card)
|
|
{
|
|
if (!isReactingToClick(_card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(cost);
|
|
_card->attemptUntap();
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AUnBlocker ::: cost : " << cost << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
|
|
AUnBlocker * clone() const
|
|
{
|
|
AUnBlocker * a = NEW AUnBlocker(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
};
|
|
|
|
//Protection From (creature/aura)
|
|
class AProtectionFrom: public MTGAbility
|
|
{
|
|
public:
|
|
TargetChooser * fromTc;
|
|
AProtectionFrom(int id, MTGCardInstance * _source, MTGCardInstance * _target, TargetChooser *fromTc) :
|
|
MTGAbility(id, _source, _target), fromTc(fromTc)
|
|
{
|
|
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->addProtection(fromTc);
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
((MTGCardInstance *) target)->removeProtection(fromTc);
|
|
return 1;
|
|
}
|
|
|
|
AProtectionFrom * clone() const
|
|
{
|
|
AProtectionFrom * a = NEW AProtectionFrom(*this);
|
|
a->fromTc = fromTc->clone();
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
~AProtectionFrom()
|
|
{
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
};
|
|
|
|
//Can't be blocked by...
|
|
class ACantBeBlockedBy: public MTGAbility
|
|
{
|
|
public:
|
|
TargetChooser * fromTc;
|
|
ACantBeBlockedBy(int id, MTGCardInstance * _source, MTGCardInstance * _target, TargetChooser *fromTc) :
|
|
MTGAbility(id, _source, _target), fromTc(fromTc)
|
|
{
|
|
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->addCantBeBlockedBy(fromTc);
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
((MTGCardInstance *) target)->removeCantBeBlockedBy(fromTc);
|
|
return 1;
|
|
}
|
|
|
|
ACantBeBlockedBy * clone() const
|
|
{
|
|
ACantBeBlockedBy * a = NEW ACantBeBlockedBy(*this);
|
|
a->fromTc = fromTc->clone();
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
~ACantBeBlockedBy()
|
|
{
|
|
SAFE_DELETE(fromTc);
|
|
}
|
|
|
|
};
|
|
|
|
//Alteration of Power and Toughness (enchantments)
|
|
class APowerToughnessModifier: public MTGAbility
|
|
{
|
|
public:
|
|
WParsedPT * wppt;
|
|
APowerToughnessModifier(int id, MTGCardInstance * _source, MTGCardInstance * _target, WParsedPT * wppt) :
|
|
MTGAbility(id, _source, _target), wppt(wppt)
|
|
{
|
|
aType = MTGAbility::STANDARD_PUMP;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->power += wppt->power.getValue();
|
|
_target->addToToughness(wppt->toughness.getValue());
|
|
if(_target->has(Constants::INDESTRUCTIBLE) && wppt->toughness.getValue() < 0 && _target->toughness <= 0)
|
|
{
|
|
_target->controller()->game->putInGraveyard(_target);
|
|
}
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
((MTGCardInstance *) target)->power -= wppt->power.getValue();
|
|
((MTGCardInstance *) target)->addToToughness(-wppt->toughness.getValue());
|
|
return 1;
|
|
}
|
|
|
|
APowerToughnessModifier * clone() const
|
|
{
|
|
APowerToughnessModifier * a = NEW APowerToughnessModifier(*this);
|
|
a->wppt = NEW WParsedPT(*(a->wppt));
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
~APowerToughnessModifier()
|
|
{
|
|
delete (wppt);
|
|
}
|
|
|
|
};
|
|
|
|
//Alteration of Power and toughness until end of turn (instant)
|
|
class AInstantPowerToughnessModifierUntilEOT: public InstantAbility
|
|
{
|
|
public:
|
|
WParsedPT * wppt;
|
|
string s;
|
|
AInstantPowerToughnessModifierUntilEOT(int _id, MTGCardInstance * _source, MTGCardInstance * _target, WParsedPT * wppt) :
|
|
InstantAbility(_id, _source, _target), wppt(wppt)
|
|
{
|
|
aType = MTGAbility::STANDARD_PUMP;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
((MTGCardInstance *) target)->power += wppt->power.getValue();
|
|
((MTGCardInstance *) target)->addToToughness(wppt->toughness.getValue());
|
|
if(((MTGCardInstance *) target)->has(Constants::INDESTRUCTIBLE) && wppt->toughness.getValue() < 0 && ((MTGCardInstance *) target)->toughness <= 0)
|
|
{
|
|
((MTGCardInstance *) target)->controller()->game->putInGraveyard(((MTGCardInstance *) target));
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
((MTGCardInstance *) target)->power -= wppt->power.getValue();
|
|
((MTGCardInstance *) target)->addToToughness(-wppt->toughness.getValue());
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
sprintf(menuText, "%i/%i", wppt->power.getValue(), wppt->toughness.getValue());
|
|
return menuText;
|
|
}
|
|
|
|
AInstantPowerToughnessModifierUntilEOT * clone() const
|
|
{
|
|
AInstantPowerToughnessModifierUntilEOT * a = NEW AInstantPowerToughnessModifierUntilEOT(*this);
|
|
a->wppt = NEW WParsedPT(*(a->wppt));
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
~AInstantPowerToughnessModifierUntilEOT()
|
|
{
|
|
delete wppt;
|
|
}
|
|
};
|
|
|
|
//Alteration of Power and Toughness until end of turn (Aura)
|
|
class APowerToughnessModifierUntilEndOfTurn: public ActivatedAbility
|
|
{
|
|
public:
|
|
AInstantPowerToughnessModifierUntilEOT * ability;
|
|
int counters;
|
|
int maxcounters;
|
|
APowerToughnessModifierUntilEndOfTurn(int id, MTGCardInstance * _source, MTGCardInstance * _target, WParsedPT * wppt,
|
|
ManaCost * _cost = NULL, int _maxcounters = 0) :
|
|
ActivatedAbility(id, _source, _cost, 0, 0), maxcounters(_maxcounters)
|
|
{
|
|
counters = 0;
|
|
target = _target;
|
|
ability = NEW AInstantPowerToughnessModifierUntilEOT(id, _source, _target, wppt);
|
|
aType = MTGAbility::STANDARD_PUMP;
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * cost = NULL)
|
|
{
|
|
//The upper level "GenericTargetAbility" takes care of the click so we always return 0 here
|
|
return 0;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_AFTER_EOT)
|
|
{
|
|
counters = 0;
|
|
}
|
|
ActivatedAbility::Update(dt);
|
|
}
|
|
|
|
int fireAbility()
|
|
{
|
|
return resolve();
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
/* int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){
|
|
if (!ActivatedAbility::isReactingToClick(card,mana)) return 0;
|
|
return (!maxcounters || (counters < maxcounters));
|
|
}*/
|
|
|
|
int resolve()
|
|
{
|
|
MTGAbility * a = ability->clone();
|
|
a->target = target;
|
|
a->addToGame();
|
|
counters++;
|
|
return 1;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
resolve();
|
|
return ActivatedAbility::addToGame();
|
|
}
|
|
|
|
APowerToughnessModifierUntilEndOfTurn * clone() const
|
|
{
|
|
APowerToughnessModifierUntilEndOfTurn * a = NEW APowerToughnessModifierUntilEndOfTurn(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
~APowerToughnessModifierUntilEndOfTurn()
|
|
{
|
|
if (!isClone)
|
|
SAFE_DELETE(ability);
|
|
}
|
|
};
|
|
|
|
class GenericInstantAbility: public InstantAbility, public NestedAbility
|
|
{
|
|
public:
|
|
GenericInstantAbility(int _id, MTGCardInstance * _source, Damageable * _target, MTGAbility * ability) :
|
|
InstantAbility(_id, _source, _target), NestedAbility(ability)
|
|
{
|
|
ability->target = _target;
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
ability->forceDestroy = -1;
|
|
ability->addToGame();
|
|
return InstantAbility::addToGame();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
ability->forceDestroy = 1;
|
|
return InstantAbility::destroy();
|
|
}
|
|
|
|
GenericInstantAbility * clone() const
|
|
{
|
|
GenericInstantAbility * a = NEW GenericInstantAbility(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
};
|
|
|
|
//Circle of Protections
|
|
class ACircleOfProtection: public TargetAbility
|
|
{
|
|
protected:
|
|
map<ReplacementEffect*, int> current;
|
|
public:
|
|
ACircleOfProtection(int _id, MTGCardInstance * source, int _color) :
|
|
TargetAbility(_id, source, NEW SpellOrPermanentTargetChooser(source, _color), NEW ManaCost(), 0, 0)
|
|
{
|
|
cost->add(Constants::MTG_COLOR_ARTIFACT, 1);
|
|
tc->targetter = NULL; //Circle of Protection doesn't use the word "source"
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _target = NULL;
|
|
if (!(_target = tc->getNextCardTarget()))
|
|
{
|
|
Spell * starget = tc->getNextSpellTarget();
|
|
_target = starget->source;
|
|
}
|
|
if (!_target) return 0;
|
|
REDamagePrevention * re = NEW REDamagePrevention(this, NEW CardTargetChooser(_target, NULL), NEW PlayerTargetChooser(0, 1,
|
|
source->controller()));
|
|
current[re] = 1;
|
|
game->replacementEffects->add(re);
|
|
return 1;
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
for (map<ReplacementEffect*, int>::iterator it = current.begin(); it != current.end(); it++)
|
|
{
|
|
ReplacementEffect* re = (*it).first;
|
|
game->replacementEffects->remove(re);
|
|
delete re;
|
|
}
|
|
current.clear();
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UNTAP) clear();
|
|
TargetAbility::Update(dt);
|
|
}
|
|
|
|
~ACircleOfProtection()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ACircleOfProtection ::: (";
|
|
return TargetAbility::toString(out) << ")";
|
|
}
|
|
ACircleOfProtection * clone() const
|
|
{
|
|
ACircleOfProtection * a = NEW ACircleOfProtection(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Basic regeneration mechanism for a Mana cost
|
|
class AStandardRegenerate: public ActivatedAbility
|
|
{
|
|
public:
|
|
AStandardRegenerate(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost = NULL) :
|
|
ActivatedAbility(_id, _source, _cost, 0, 0)
|
|
{
|
|
target = _target;
|
|
aType = MTGAbility::STANDARD_REGENERATE;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->regenerate();
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return "Regenerate";
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AStandardRegenerate ::: (";
|
|
return ActivatedAbility::toString(out) << ")";
|
|
}
|
|
AStandardRegenerate * clone() const
|
|
{
|
|
AStandardRegenerate * a = NEW AStandardRegenerate(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Aura Enchantments that provide controller of target life or damages at a given phase of their turn
|
|
class ARegularLifeModifierAura: public MTGAbility
|
|
{
|
|
public:
|
|
int life;
|
|
int phase;
|
|
int onlyIfTargetTapped;
|
|
ARegularLifeModifierAura(int id, MTGCardInstance * _source, MTGCardInstance * _target, int _phase, int _life,
|
|
int _onlyIfTargetTapped = 0) :
|
|
MTGAbility(id, _source, _target), life(_life), phase(_phase), onlyIfTargetTapped(_onlyIfTargetTapped)
|
|
{
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == phase && game->currentPlayer == ((MTGCardInstance *) target)->controller())
|
|
{
|
|
if (!onlyIfTargetTapped || ((MTGCardInstance *) target)->isTapped())
|
|
{
|
|
if (life > 0)
|
|
{
|
|
game->currentPlayer->life += life;
|
|
}
|
|
else
|
|
{
|
|
game->mLayers->stackLayer()->addDamage(source, game->currentPlayer, -life);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ARegularLifeModifierAura ::: life : " << life << " ; phase : " << phase << " ; onlyIfTargetTapped : "
|
|
<< onlyIfTargetTapped << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
ARegularLifeModifierAura * clone() const
|
|
{
|
|
ARegularLifeModifierAura * a = NEW ARegularLifeModifierAura(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//ExaltedAbility (Shards of Alara)
|
|
class AExalted: public TriggeredAbility
|
|
{
|
|
public:
|
|
int power, toughness;
|
|
MTGCardInstance * luckyWinner;
|
|
AExalted(int _id, MTGCardInstance * _source, int _power = 1, int _toughness = 1) :
|
|
TriggeredAbility(_id, _source), power(_power), toughness(_toughness)
|
|
{
|
|
luckyWinner = NULL;
|
|
}
|
|
|
|
int triggerOnEvent(WEvent * event)
|
|
{
|
|
if (WEventPhaseChange* pe = dynamic_cast<WEventPhaseChange*>(event))
|
|
{
|
|
if (luckyWinner && Constants::MTG_PHASE_AFTER_EOT == pe->from->id)
|
|
{
|
|
luckyWinner->addToToughness(-toughness);
|
|
luckyWinner->power -= power;
|
|
luckyWinner = NULL;
|
|
}
|
|
|
|
if (Constants::MTG_PHASE_COMBATATTACKERS == pe->from->id)
|
|
{
|
|
int nbattackers = 0;
|
|
MTGGameZone * z = source->controller()->game->inPlay;
|
|
int nbcards = z->nb_cards;
|
|
for (int i = 0; i < nbcards; ++i)
|
|
{
|
|
MTGCardInstance * c = z->cards[i];
|
|
if (c->attacker)
|
|
{
|
|
nbattackers++;
|
|
luckyWinner = c;
|
|
}
|
|
}
|
|
if (nbattackers == 1)
|
|
return 1;
|
|
else
|
|
luckyWinner = NULL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
if (!luckyWinner) return 0;
|
|
luckyWinner->addToToughness(toughness);
|
|
luckyWinner->power += power;
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return "Exalted";
|
|
}
|
|
|
|
AExalted * clone() const
|
|
{
|
|
AExalted * a = NEW AExalted(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Converts lands to creatures (Kormus bell, Living lands)
|
|
class AConvertLandToCreatures: public ListMaintainerAbility
|
|
{
|
|
public:
|
|
int type;
|
|
int power, toughness;
|
|
AConvertLandToCreatures(int _id, MTGCardInstance * _source, const char * _type, int _power = 1, int _toughness = 1) :
|
|
ListMaintainerAbility(_id, _source), power(_power), toughness(_toughness)
|
|
{
|
|
type = Subtypes::subtypesList->find(_type);
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card)
|
|
{
|
|
if (card->hasType(type) && game->isInPlay(card)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card)
|
|
{
|
|
card->power = 1;
|
|
card->setToughness(1);
|
|
card->setSubtype("creature");
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card)
|
|
{
|
|
card->removeType("creature");
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AConvertLandToCreatures ::: power : " << power << " ; toughness : " << toughness << " ; type : " << type << " (";
|
|
return ListMaintainerAbility::toString(out) << ")";
|
|
}
|
|
AConvertLandToCreatures * clone() const
|
|
{
|
|
AConvertLandToCreatures * a = NEW AConvertLandToCreatures(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Generic Kird Ape
|
|
class AAsLongAs: public ListMaintainerAbility, public NestedAbility
|
|
{
|
|
public:
|
|
MTGAbility * a;
|
|
int includeSelf;
|
|
int mini, maxi;
|
|
AAsLongAs(int _id, MTGCardInstance * _source, Damageable * _target, TargetChooser * _tc, int _includeSelf,
|
|
MTGAbility * ability, int mini = 0, int maxi = 0) :
|
|
ListMaintainerAbility(_id, _source, _target), NestedAbility(ability), mini(mini), maxi(maxi)
|
|
{
|
|
tc = _tc;
|
|
includeSelf = _includeSelf;
|
|
tc->targetter = NULL;
|
|
ability->source = source;
|
|
ability->target = target;
|
|
a = NULL;
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card)
|
|
{
|
|
if(card->isPhased || source->isPhased)
|
|
return 0;
|
|
int size = 0;
|
|
size = (int) cards.size();
|
|
if (includeSelf && maxi && card == source && size > maxi)
|
|
{
|
|
removed(card);
|
|
}
|
|
if ((includeSelf || card != source) && tc->canTarget(card))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
//TODO check if ability is oneShot ?
|
|
updateTargets();
|
|
int size = (int) cards.size();
|
|
if (maxi && size < maxi && (!mini || size > mini)) addAbilityToGame(); //special case for 0
|
|
if (ability->oneShot) a = NULL; //allows to call the effect several times
|
|
cards.clear();
|
|
players.clear();
|
|
return 1;
|
|
}
|
|
|
|
int addAbilityToGame()
|
|
{
|
|
if (a) return 0;
|
|
a = ability->clone();
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
delete (a);
|
|
}
|
|
else
|
|
{
|
|
a->addToGame();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removeAbilityFromGame()
|
|
{
|
|
if (!a) return 0;
|
|
game->removeObserver(a);
|
|
a = NULL;
|
|
return 1;
|
|
}
|
|
|
|
int _added(Damageable * d)
|
|
{
|
|
int size = (int) cards.size();
|
|
if (maxi && size >= maxi) return removeAbilityFromGame();
|
|
if (maxi) return 0;
|
|
if (size <= mini) return 0;
|
|
return addAbilityToGame();
|
|
}
|
|
|
|
int added(MTGCardInstance * card)
|
|
{
|
|
return _added(card);
|
|
}
|
|
|
|
int added(Player * p)
|
|
{
|
|
return _added(p);
|
|
}
|
|
|
|
int removed(MTGCardInstance * card)
|
|
{
|
|
size_t size = cards.size();
|
|
if (maxi && (int) size < maxi) return addAbilityToGame();
|
|
if (mini && (int) size > mini) return 0;
|
|
if (mini && (int) size <= mini) return removeAbilityFromGame();
|
|
if (!mini && !maxi && size != 0) return 0;
|
|
return removeAbilityFromGame();
|
|
}
|
|
|
|
~AAsLongAs()
|
|
{
|
|
if (!isClone)
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
if(ability)
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
else
|
|
{
|
|
return "Ability";
|
|
}
|
|
}
|
|
|
|
AAsLongAs * clone() const
|
|
{
|
|
AAsLongAs * a = NEW AAsLongAs(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Lords (Merfolk lord...) give power and toughness to OTHER creatures of their type, they can give them special abilities, regeneration
|
|
class ALord: public ListMaintainerAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int includeSelf;
|
|
map<Damageable *, MTGAbility *> abilities;
|
|
|
|
ALord(int _id, MTGCardInstance * card, TargetChooser * _tc, int _includeSelf, MTGAbility * a) :
|
|
ListMaintainerAbility(_id, card), NestedAbility(a)
|
|
{
|
|
tc = _tc;
|
|
tc->targetter = NULL;
|
|
includeSelf = _includeSelf;
|
|
if(ability->aType == MTGAbility::STANDARD_PREVENT)
|
|
aType = MTGAbility::STANDARD_PREVENT;
|
|
}
|
|
|
|
int canBeInList(Player *p)
|
|
{
|
|
if (tc->canTarget(p)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card)
|
|
{
|
|
if(card->isPhased || source->isPhased)
|
|
return 0;
|
|
if ((includeSelf || card != source) && tc->canTarget(card)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
//TODO check if ability is oneShot ?
|
|
updateTargets();
|
|
cards.clear();
|
|
players.clear();
|
|
return 1;
|
|
}
|
|
|
|
int _added(Damageable * d)
|
|
{
|
|
MTGAbility * a = ability->clone();
|
|
a->target = d;
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
delete (a);
|
|
}
|
|
else
|
|
{
|
|
if (d->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE)
|
|
{
|
|
a->source = (MTGCardInstance *) d;
|
|
}
|
|
if (oneShot)
|
|
{
|
|
MTGAbility * wrapper = NEW GenericInstantAbility(1, source, d, a);
|
|
wrapper->addToGame();
|
|
}
|
|
else
|
|
{
|
|
a->addToGame();
|
|
abilities[d] = a;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int added(MTGCardInstance * card)
|
|
{
|
|
return _added(card);
|
|
}
|
|
|
|
int added(Player * p)
|
|
{
|
|
return _added(p);
|
|
}
|
|
|
|
int removed(MTGCardInstance * card)
|
|
{
|
|
if (abilities.find(card) != abilities.end())
|
|
{
|
|
game->removeObserver(abilities[card]);
|
|
abilities.erase(card);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
~ALord()
|
|
{
|
|
if (!isClone)
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
if (AAMover * move = dynamic_cast<AAMover *>(ability))
|
|
{
|
|
MTGGameZone * dest = move->destinationZone();
|
|
GameObserver * g = GameObserver::GetInstance();
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
// Move card to hand
|
|
if (dest == g->players[i]->game->hand)
|
|
{
|
|
if (tc->targetsZone(g->players[i]->game->inPlay))
|
|
{
|
|
return "Bounce";
|
|
}
|
|
else if (tc->targetsZone(g->players[i]->game->graveyard))
|
|
{
|
|
return "Reclaim";
|
|
}
|
|
else if (tc->targetsZone(g->opponent()->game->hand))
|
|
{
|
|
return "Steal";
|
|
}
|
|
}
|
|
// Move card to graveyard
|
|
else if (dest == g->players[i]->game->graveyard)
|
|
{
|
|
if (tc->targetsZone(g->players[i]->game->inPlay))
|
|
{
|
|
return "Sacrifice";
|
|
}
|
|
else if (tc->targetsZone(g->players[i]->game->hand))
|
|
{
|
|
return "Discard";
|
|
}
|
|
else if (tc->targetsZone(g->opponent()->game->hand))
|
|
{
|
|
return "Opponent Discards";
|
|
}
|
|
}
|
|
// move card to library
|
|
else if (dest == g->players[i]->game->library)
|
|
{
|
|
if (tc->targetsZone(g->players[i]->game->graveyard))
|
|
{
|
|
return "Recycle";
|
|
}
|
|
else
|
|
{
|
|
return "Put in Library";
|
|
}
|
|
}
|
|
// move card to battlefield
|
|
else if (dest == g->players[i]->game->battlefield && tc->targetsZone(g->players[i]->game->graveyard))
|
|
{
|
|
return "Reanimate";
|
|
}
|
|
// move card in play ( different from battlefield? )
|
|
else if (dest == g->players[i]->game->inPlay)
|
|
{
|
|
return "Put in Play";
|
|
}
|
|
// move card into exile
|
|
else if (dest == g->players[i]->game->exile)
|
|
{
|
|
return "Exile";
|
|
}
|
|
// move card from Library
|
|
else if (tc->targetsZone(g->players[i]->game->library))
|
|
{
|
|
return "Fetch";
|
|
}
|
|
}
|
|
return "Move";
|
|
}
|
|
else
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
ALord * clone() const
|
|
{
|
|
ALord * a = NEW ALord(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//a different lord for auras and enchantments. http://code.google.com/p/wagic/issues/detail?id=244
|
|
class ATeach: public ListMaintainerAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int includeSelf;
|
|
map<Damageable *, MTGAbility *> skills;
|
|
|
|
ATeach(int _id, MTGCardInstance * card, TargetChooser * _tc, int _includeSelf, MTGAbility * a) :
|
|
ListMaintainerAbility(_id, card), NestedAbility(a)
|
|
{
|
|
tc = _tc;
|
|
tc->targetter = NULL;
|
|
includeSelf = NULL;
|
|
aType = MTGAbility::STANDARD_TEACH;
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card)
|
|
{
|
|
if ((tc->source->hasSubtype("aura") || tc->source->hasSubtype("equipment") || tc->source->hasSubtype("instant")
|
|
|| tc->source->hasSubtype("sorcery")) && tc->canTarget(card) && card == tc->source->target && card != tc->source) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
updateTargets();
|
|
cards.clear();
|
|
players.clear();
|
|
return 1;
|
|
}
|
|
|
|
int added(MTGCardInstance * card)
|
|
{
|
|
return _added(card);
|
|
}
|
|
|
|
int removed(MTGCardInstance * card)
|
|
{
|
|
if (skills.find(card) != skills.end())
|
|
{
|
|
game->removeObserver(skills[card]);
|
|
skills.erase(card);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int _added(Damageable * d)
|
|
{
|
|
MTGAbility * a = ability->clone();
|
|
|
|
if (a->source->hasSubtype("aura") || a->source->hasSubtype("equipment") || a->source->hasSubtype("instant")
|
|
|| a->source->hasSubtype("sorcery"))
|
|
{
|
|
a->target = a->source->target;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
delete (a);
|
|
}
|
|
else
|
|
{
|
|
if (d->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE)
|
|
{
|
|
a->source = (MTGCardInstance *) d;
|
|
}
|
|
if (oneShot)
|
|
{
|
|
MTGAbility * wrapper = NEW GenericInstantAbility(1, source, d, a);
|
|
wrapper->addToGame();
|
|
}
|
|
else
|
|
{
|
|
skills[d] = a;
|
|
a->addToGame();
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
~ATeach()
|
|
{
|
|
if (!isClone)
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
ATeach * clone() const
|
|
{
|
|
ATeach * a = NEW ATeach(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
};
|
|
//
|
|
|
|
//equipment
|
|
class AEquip: public TargetAbility
|
|
{
|
|
public:
|
|
vector<MTGAbility *> currentAbilities;
|
|
AEquip(int _id, MTGCardInstance * _source, ManaCost * _cost = NULL, int doTap = 0, int restrictions =
|
|
ActivatedAbility::AS_SORCERY) :
|
|
TargetAbility(_id, _source, NULL, _cost, restrictions, doTap)
|
|
{
|
|
aType = MTGAbility::STANDARD_EQUIP;
|
|
}
|
|
|
|
int unequip()
|
|
{
|
|
if (source->target)
|
|
{
|
|
source->target->equipment -= 1;
|
|
}
|
|
source->target = NULL;
|
|
for (size_t i = 0; i < currentAbilities.size(); ++i)
|
|
{
|
|
MTGAbility * a = currentAbilities[i];
|
|
if (dynamic_cast<AEquip *> (a) || dynamic_cast<ATeach *> (a))
|
|
{
|
|
SAFE_DELETE(a);
|
|
continue;
|
|
}
|
|
GameObserver::GetInstance()->removeObserver(currentAbilities[i]);
|
|
}
|
|
currentAbilities.clear();
|
|
return 1;
|
|
}
|
|
|
|
int equip(MTGCardInstance * equipped)
|
|
{
|
|
source->target = equipped;
|
|
source->target->equipment += 1;
|
|
AbilityFactory af;
|
|
af.getAbilities(¤tAbilities, NULL, source);
|
|
for (size_t i = 0; i < currentAbilities.size(); ++i)
|
|
{
|
|
MTGAbility * a = currentAbilities[i];
|
|
if (dynamic_cast<AEquip *> (a)) continue;
|
|
if (dynamic_cast<ATeach *> (a)) continue;
|
|
a->addToGame();
|
|
}
|
|
return 1;
|
|
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * mTarget = tc->getNextCardTarget();
|
|
if (!mTarget) return 0;
|
|
if (mTarget == source) return 0;
|
|
unequip();
|
|
equip(mTarget);
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return "Equip";
|
|
}
|
|
|
|
int testDestroy()
|
|
{
|
|
if (source->target && !game->isInPlay(source->target)) unequip();
|
|
return TargetAbility::testDestroy();
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
unequip();
|
|
return TargetAbility::destroy();
|
|
}
|
|
|
|
AEquip * clone() const
|
|
{
|
|
AEquip * a = NEW AEquip(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
};
|
|
//Foreach (plague rats...)
|
|
class AForeach: public ListMaintainerAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int includeSelf;
|
|
int mini;
|
|
int maxi;
|
|
map<Damageable *, MTGAbility *> abilities;
|
|
AForeach(int _id, MTGCardInstance * card, Damageable * _target, TargetChooser * _tc, int _includeSelf, MTGAbility * a,
|
|
int mini = 0, int maxi = 0) :
|
|
ListMaintainerAbility(_id, card, _target), NestedAbility(a), mini(mini), maxi(maxi)
|
|
{
|
|
tc = _tc;
|
|
tc->targetter = NULL;
|
|
includeSelf = _includeSelf;
|
|
ability->target = _target;
|
|
aType = MTGAbility::FOREACH;
|
|
naType = ability->aType;
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card)
|
|
{
|
|
if(card->isPhased || source->isPhased)
|
|
return 0;
|
|
if ((includeSelf || card != source) && tc->canTarget(card)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card)
|
|
{
|
|
if (mini && cards.size() <= (size_t) mini) return 0;
|
|
if (maxi && cards.size() >= (size_t) maxi) return 0;
|
|
|
|
MTGAbility * a = ability->clone();
|
|
a->target = target;
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
delete (a);
|
|
}
|
|
else
|
|
{
|
|
a->addToGame();
|
|
abilities[card] = a;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card)
|
|
{
|
|
if (abilities.find(card) != abilities.end())
|
|
{
|
|
game->removeObserver(abilities[card]);
|
|
abilities.erase(card);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
AForeach * clone() const
|
|
{
|
|
AForeach * a = NEW AForeach(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
//TODO check if ability is oneShot ?
|
|
updateTargets();
|
|
cards.clear();
|
|
players.clear();
|
|
return 1;
|
|
}
|
|
|
|
~AForeach()
|
|
{
|
|
if (!isClone)
|
|
SAFE_DELETE(ability);
|
|
}
|
|
|
|
};
|
|
|
|
class AThis: public MTGAbility, public NestedAbility
|
|
{
|
|
public:
|
|
MTGAbility * a;
|
|
ThisDescriptor * td;
|
|
AThis(int _id, MTGCardInstance * _source, Damageable * _target, ThisDescriptor * _td, MTGAbility * ability) :
|
|
MTGAbility(_id, _source, _target), NestedAbility(ability)
|
|
{
|
|
td = _td;
|
|
ability->source = source;
|
|
ability->target = target;
|
|
a = NULL;
|
|
SAFE_DELETE(tc);
|
|
}
|
|
|
|
int removeFromGame()
|
|
{
|
|
return removeAbilityFromGame();
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
resolve();
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
//TODO check if ability is oneShot ?
|
|
int match;
|
|
match = td->match(source);
|
|
if (match > 0)
|
|
{
|
|
addAbilityToGame();
|
|
}
|
|
else
|
|
{
|
|
removeAbilityFromGame();
|
|
}
|
|
if (ability->oneShot) a = NULL; //allows to call the effect several times
|
|
return 1;
|
|
}
|
|
|
|
int addAbilityToGame()
|
|
{
|
|
if (a) return 0;
|
|
a = ability->clone();
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
delete (a);
|
|
}
|
|
else
|
|
{
|
|
a->addToGame();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removeAbilityFromGame()
|
|
{
|
|
if (!a) return 0;
|
|
game->removeObserver(a);
|
|
a = NULL;
|
|
return 1;
|
|
}
|
|
|
|
~AThis()
|
|
{
|
|
if (!isClone)
|
|
SAFE_DELETE(ability);
|
|
SAFE_DELETE(td);
|
|
}
|
|
|
|
AThis * clone() const
|
|
{
|
|
AThis * a = NEW AThis(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
class AThisForEach: public MTGAbility, public NestedAbility
|
|
{
|
|
public:
|
|
ThisDescriptor * td;
|
|
vector<MTGAbility *> abilities;
|
|
AThisForEach(int _id, MTGCardInstance * _source, Damageable * _target, ThisDescriptor * _td, MTGAbility * ability) :
|
|
MTGAbility(_id, _source, _target), NestedAbility(ability)
|
|
{
|
|
td = _td;
|
|
ability->source = source;
|
|
ability->target = target;
|
|
SAFE_DELETE(tc);
|
|
aType = FOREACH;
|
|
}
|
|
|
|
int removeFromGame()
|
|
{
|
|
return removeAbilityFromGame();
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
resolve();
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
//TODO check if ability is oneShot ?
|
|
int matches;
|
|
matches = td->match(source);
|
|
if (matches > 0)
|
|
{
|
|
if ((int) (abilities.size()) > matches)
|
|
{
|
|
removeAbilityFromGame();
|
|
}
|
|
for (int i = 0; i < matches - (int) (abilities.size()); i++)
|
|
{
|
|
addAbilityToGame();
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int addAbilityToGame()
|
|
{
|
|
MTGAbility * a = ability->clone();
|
|
a->target = target;
|
|
if (a->oneShot)
|
|
{
|
|
a->resolve();
|
|
delete (a);
|
|
}
|
|
else
|
|
{
|
|
a->addToGame();
|
|
abilities.push_back(a);
|
|
//abilities[abilities.size()] = a;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removeAbilityFromGame()
|
|
{
|
|
for (int i = abilities.size(); i > 0; i--)
|
|
{
|
|
game->removeObserver(abilities[i - 1]);
|
|
}
|
|
abilities.clear();
|
|
return 1;
|
|
}
|
|
|
|
~AThisForEach()
|
|
{
|
|
if (!isClone)
|
|
{
|
|
SAFE_DELETE(ability);
|
|
SAFE_DELETE(td);
|
|
}
|
|
if (abilities.size())
|
|
{
|
|
removeAbilityFromGame();
|
|
}
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return ability->getMenuText();
|
|
}
|
|
|
|
AThisForEach * clone() const
|
|
{
|
|
AThisForEach * a = NEW AThisForEach(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
//lifeset
|
|
class AALifeSet: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
WParsedInt * life;
|
|
|
|
AALifeSet(int _id, MTGCardInstance * _source, Targetable * _target, WParsedInt * life, ManaCost * _cost = NULL, int doTap = 0,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AALifeSet * clone() const;
|
|
~AALifeSet();
|
|
|
|
};
|
|
|
|
//lifesetend
|
|
|
|
class AADamager: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
WParsedInt * damage;
|
|
WParsedInt * RefreshedDamage;
|
|
string d;
|
|
|
|
AADamager(int _id, MTGCardInstance * _source, Targetable * _target,WParsedInt * damage, string d, ManaCost * _cost = NULL,
|
|
int doTap = 0, int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AADamager * clone() const;
|
|
~AADamager();
|
|
|
|
};
|
|
|
|
//prevent next damage
|
|
class AADamagePrevent: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
int preventing;
|
|
|
|
AADamagePrevent(int _id, MTGCardInstance * _source, Targetable * _target, int preventing, ManaCost * _cost = NULL, int doTap =
|
|
0, int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AADamagePrevent * clone() const;
|
|
~AADamagePrevent();
|
|
};
|
|
|
|
//poison removel
|
|
class AAAlterPoison: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
int poison;
|
|
|
|
AAAlterPoison(int _id, MTGCardInstance * _source, Targetable * _target, int poison, ManaCost * _cost = NULL, int doTap = 0,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AAAlterPoison * clone() const;
|
|
~AAAlterPoison();
|
|
};
|
|
/* Standard Damager, can choose a NEW target each time the price is paid */
|
|
class TADamager: public TargetAbility
|
|
{
|
|
public:
|
|
|
|
TADamager(int id, MTGCardInstance * card, ManaCost * _cost,WParsedInt *damage, string d, TargetChooser * _tc = NULL, int _tap = 0) :
|
|
TargetAbility(id, card, _tc, _cost, 0, _tap)
|
|
{
|
|
if (!tc) tc = NEW DamageableTargetChooser(card);
|
|
ability = NEW AADamager(id, card, NULL,damage, d);
|
|
}
|
|
|
|
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);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AATapper * clone() const;
|
|
};
|
|
|
|
/* 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);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AAUntapper * clone() const;
|
|
};
|
|
|
|
/* set max level up on a levelup creature this is an Ai hint ability, no effect for players.*/
|
|
class AAWhatsMax: public ActivatedAbility
|
|
{
|
|
public:
|
|
int value;
|
|
|
|
AAWhatsMax(int id, MTGCardInstance * card, MTGCardInstance * source, ManaCost * _cost = NULL, int doTap = 0, int value = 0);
|
|
int resolve();
|
|
AAWhatsMax * clone() const;
|
|
};
|
|
|
|
/* Can prevent a card from untapping next untap */
|
|
class AAFrozen: public ActivatedAbility
|
|
{
|
|
public:
|
|
AAFrozen(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL, int doTap = 0);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AAFrozen * clone() const;
|
|
};
|
|
/* ghetto new target*/
|
|
class AANewTarget: public ActivatedAbility
|
|
{
|
|
public:
|
|
AANewTarget(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL, int doTap = 0);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AANewTarget * clone() const;
|
|
};
|
|
/* morph*/
|
|
class AAMorph: public ActivatedAbility
|
|
{
|
|
public:
|
|
vector<MTGAbility *> currentAbilities;
|
|
AAMorph(int id, MTGCardInstance * card, MTGCardInstance * _target, ManaCost * _cost = NULL, int doTap = 0);
|
|
int resolve();
|
|
int testDestroy();
|
|
const char * getMenuText();
|
|
AAMorph * clone() const;
|
|
};
|
|
/* dynamic ability build*/
|
|
class AADYNAMIC: public ActivatedAbility
|
|
{
|
|
public:
|
|
int type;
|
|
int effect;
|
|
int who;
|
|
int sourceamount;
|
|
int targetamount;
|
|
int amountsource;
|
|
bool eachother;
|
|
bool tosrc;
|
|
MTGCardInstance * OriginalSrc;
|
|
MTGCardInstance * storedTarget;
|
|
MTGAbility * storedAbility;
|
|
MTGAbility * clonedStored;
|
|
string menu;
|
|
|
|
AADYNAMIC(int id, MTGCardInstance * card, Damageable * _target,int type = 0,int effect = 0,int who = 0,int amountsource = 1,MTGAbility * storedAbility = NULL, ManaCost * _cost = NULL, int doTap = 0);
|
|
int resolve();
|
|
int activateStored();
|
|
const char * getMenuText();
|
|
AADYNAMIC * clone() const;
|
|
~AADYNAMIC();
|
|
};
|
|
|
|
/* removes a card and all with the same name as it from the game */
|
|
class AAEradicate: public ActivatedAbility
|
|
{
|
|
public:
|
|
int type;
|
|
AAEradicate(int id, MTGCardInstance * card, MTGCardInstance * _target,int type = 0, ManaCost * _cost = NULL, int doTap = 0);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AAEradicate * clone() const;
|
|
};
|
|
|
|
/* switch power and toughness of target */
|
|
class ASwapPT: public InstantAbility
|
|
{
|
|
public:
|
|
int oldpower;
|
|
int oldtoughness;
|
|
ASwapPT(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
InstantAbility(_id, _source, _target)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
while (_target->next)
|
|
_target = _target->next; //This is for cards such as rampant growth
|
|
oldpower = _target->power;
|
|
oldtoughness = _target->toughness;
|
|
|
|
_target->addToToughness(oldpower);
|
|
_target->addToToughness(-oldtoughness);
|
|
_target->power = oldtoughness;
|
|
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target)
|
|
{
|
|
while (_target->next)
|
|
_target = _target->next; //This is for cards such as rampant growth
|
|
oldpower = _target->power;
|
|
oldtoughness = _target->toughness;
|
|
|
|
_target->addToToughness(oldpower);
|
|
_target->addToToughness(-oldtoughness);
|
|
_target->power = oldtoughness;
|
|
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
const char * getMenuText()
|
|
{
|
|
return "Swap power and toughness";
|
|
}
|
|
ASwapPT * clone() const
|
|
{
|
|
ASwapPT * a = NEW ASwapPT(*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++)
|
|
{
|
|
if (game->isInPlay(opponents[i]))
|
|
opponents[i]->destroy();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int testDestroy()
|
|
{
|
|
if (!game->isInPlay(source) && currentPhase != Constants::MTG_PHASE_UNTAP)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return MTGAbility::testDestroy();
|
|
}
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AOldSchoolDeathtouch ::: opponents : " << opponents << " ; nbOpponents : " << nbOpponents << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AOldSchoolDeathtouch * clone() const
|
|
{
|
|
AOldSchoolDeathtouch * a = NEW AOldSchoolDeathtouch(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//reset card cost-----------------------------------------
|
|
class AResetCost: public MTGAbility
|
|
{
|
|
public:
|
|
AResetCost(int id, MTGCardInstance * source, MTGCardInstance * target) :
|
|
MTGAbility(id, source, target)
|
|
{
|
|
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
_target->controller()->game->putInZone(_target, _target->controller()->game->hand, _target->controller()->game->hand);
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
AResetCost * clone() const
|
|
{
|
|
AResetCost * a = NEW AResetCost(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
~AResetCost()
|
|
{
|
|
}
|
|
};
|
|
//bloodthirst ability------------------------------------------
|
|
class ABloodThirst: public MTGAbility
|
|
{
|
|
public:
|
|
int amount;
|
|
ABloodThirst(int id, MTGCardInstance * source, MTGCardInstance * target, int amount) :
|
|
MTGAbility(id, source, target), amount(amount)
|
|
{
|
|
}
|
|
|
|
int addToGame()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
amount;
|
|
for (int i = 0; i < amount; i++)
|
|
{
|
|
if (_target->controller()->opponent()->damaged() > 0)
|
|
{
|
|
_target->counters->addCounter(1, 1);
|
|
}
|
|
}
|
|
return MTGAbility::addToGame();
|
|
}
|
|
|
|
ABloodThirst * clone() const
|
|
{
|
|
ABloodThirst * a = NEW ABloodThirst(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
~ABloodThirst()
|
|
{
|
|
}
|
|
};
|
|
|
|
//reduce or increase manacost of target by color:amount------------------------------------------
|
|
class AAlterCost: public MTGAbility
|
|
{
|
|
public:
|
|
int amount;
|
|
int type;
|
|
AAlterCost(int id, MTGCardInstance * source, MTGCardInstance * target, int amount, int type);
|
|
int addToGame();
|
|
AAlterCost * clone() const;
|
|
~AAlterCost();
|
|
};
|
|
|
|
//------------------------------------
|
|
class ATransformer: public MTGAbility
|
|
{
|
|
public:
|
|
list<int> abilities;
|
|
list<int> types;
|
|
list<int> colors;
|
|
list<int> oldcolors;
|
|
list<int> oldtypes;
|
|
bool remove;
|
|
string menu;
|
|
int newpower;
|
|
bool newpowerfound;
|
|
int oldpower;
|
|
int newtoughness;
|
|
bool newtoughnessfound;
|
|
int oldtoughness;
|
|
|
|
|
|
ATransformer(int id, MTGCardInstance * source, MTGCardInstance * target, string stypes, string sabilities,int newpower,bool newpowerfound,int newtoughness,bool newtoughnessfound);
|
|
int addToGame();
|
|
int destroy();
|
|
const char * getMenuText();
|
|
ATransformer * clone() const;
|
|
~ATransformer();
|
|
};
|
|
|
|
//transforms forever class
|
|
class AForeverTransformer: public MTGAbility
|
|
{
|
|
public:
|
|
list<int> abilities;
|
|
list<int> types;
|
|
list<int> colors;
|
|
string menu;
|
|
int newpower;
|
|
bool newpowerfound;
|
|
int oldpower;
|
|
int newtoughness;
|
|
bool newtoughnessfound;
|
|
int oldtoughness;
|
|
bool remove;
|
|
|
|
AForeverTransformer(int id, MTGCardInstance * source, MTGCardInstance * target, string stypes, string sabilities,int newpower,bool newpowerfound,int newtoughness,bool newtoughnessfound);
|
|
int addToGame();
|
|
const char * getMenuText();
|
|
AForeverTransformer * clone() const;
|
|
~AForeverTransformer();
|
|
};
|
|
|
|
//Adds types/abilities/changes color to a card (until end of turn)
|
|
class ATransformerUEOT: public InstantAbility
|
|
{
|
|
public:
|
|
ATransformer * ability;
|
|
int newpower;
|
|
bool newpowerfound;
|
|
int newtoughness;
|
|
bool newtoughnessfound;
|
|
|
|
ATransformerUEOT(int id, MTGCardInstance * source, MTGCardInstance * target, string types, string abilities,int newpower,bool newpowerfound,int newtoughness,bool newtoughnessfound);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
ATransformerUEOT * clone() const;
|
|
~ATransformerUEOT();
|
|
};
|
|
|
|
//transforms forever
|
|
class ATransformerFOREVER: public InstantAbility
|
|
{
|
|
public:
|
|
AForeverTransformer * ability;
|
|
int newpower;
|
|
bool newpowerfound;
|
|
int newtoughness;
|
|
bool newtoughnessfound;
|
|
|
|
ATransformerFOREVER(int id, MTGCardInstance * source, MTGCardInstance * target, string types, string abilities,int newpower = 0,bool newpowerfound = false,int newtoughness = 0,bool newtoughnessfound = false);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
ATransformerFOREVER * clone() const;
|
|
~ATransformerFOREVER();
|
|
};
|
|
|
|
//switch p/t ueot
|
|
class ASwapPTUEOT: public InstantAbility
|
|
{
|
|
public:
|
|
ASwapPT * ability;
|
|
ASwapPTUEOT(int id, MTGCardInstance * source, MTGCardInstance * target);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
ASwapPTUEOT * clone() const;
|
|
~ASwapPTUEOT();
|
|
};
|
|
|
|
//becomes ability
|
|
//Adds types/abilities/P/T to a card (aura)
|
|
class ABecomes: public MTGAbility
|
|
{
|
|
public:
|
|
list<int> abilities;
|
|
list<int> types;
|
|
list<int> colors;
|
|
WParsedPT * wppt;
|
|
string menu;
|
|
ABecomes(int id, MTGCardInstance * source, MTGCardInstance * target, string stypes, WParsedPT * wppt, string sabilities);
|
|
int addToGame();
|
|
int destroy();
|
|
const char * getMenuText();
|
|
ABecomes * clone() const;
|
|
~ABecomes();
|
|
|
|
};
|
|
|
|
//Adds types/abilities/P/T to a card (until end of turn)
|
|
class ABecomesUEOT: public InstantAbility
|
|
{
|
|
public:
|
|
ABecomes * ability;
|
|
ABecomesUEOT(int id, MTGCardInstance * source, MTGCardInstance * target, string types, WParsedPT * wpt, string abilities);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
ABecomesUEOT * clone() const;
|
|
~ABecomesUEOT();
|
|
|
|
};
|
|
|
|
class APreventDamageTypes: public MTGAbility
|
|
{
|
|
public:
|
|
string to, from;
|
|
REDamagePrevention * re;
|
|
int type;
|
|
|
|
APreventDamageTypes(int id, MTGCardInstance * source, string to, string from, int type = 0);
|
|
int addToGame();
|
|
int destroy();
|
|
APreventDamageTypes * clone() const;
|
|
~APreventDamageTypes();
|
|
};
|
|
|
|
//Adds types/abilities/P/T to a card (until end of turn)
|
|
class APreventDamageTypesUEOT: public InstantAbility
|
|
{
|
|
public:
|
|
APreventDamageTypes * ability;
|
|
vector<APreventDamageTypes *> clones;
|
|
int type;
|
|
APreventDamageTypesUEOT(int id, MTGCardInstance * source, string to, string from, int type = 0);
|
|
int resolve();
|
|
int destroy();
|
|
const char * getMenuText();
|
|
APreventDamageTypesUEOT * clone() const;
|
|
~APreventDamageTypesUEOT();
|
|
};
|
|
|
|
//Upkeep Cost
|
|
class AUpkeep: public ActivatedAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int paidThisTurn;
|
|
int phase;
|
|
int once;
|
|
bool Cumulative;
|
|
int currentage;
|
|
|
|
AUpkeep(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int _tap = 0, int restrictions = 0, int _phase =
|
|
Constants::MTG_PHASE_UPKEEP, int _once = 0,bool Cumulative = false);
|
|
void Update(float dt);
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
virtual ostream& toString(ostream& out) const;
|
|
AUpkeep * clone() const;
|
|
~AUpkeep();
|
|
};
|
|
|
|
//phase based actions
|
|
class APhaseAction: public MTGAbility, public NestedAbility
|
|
{
|
|
public:
|
|
int phase;
|
|
bool forcedestroy;
|
|
bool next;
|
|
Player * abilityOwner;
|
|
|
|
APhaseAction(int _id, MTGCardInstance * card, MTGCardInstance * target, MTGAbility * a, int _tap = 0, int restrictions = 0, int _phase =
|
|
Constants::MTG_PHASE_UPKEEP,bool forcedestroy = false,bool next = true);
|
|
void Update(float dt);
|
|
int resolve();
|
|
int removeAbility();
|
|
const char * getMenuText();
|
|
APhaseAction * clone() const;
|
|
~APhaseAction();
|
|
};
|
|
|
|
//Adds types/abilities/P/T to a card (until end of turn)
|
|
class APhaseActionGeneric: public InstantAbility
|
|
{
|
|
public:
|
|
APhaseAction * ability;
|
|
APhaseActionGeneric(int _id, MTGCardInstance * card, MTGCardInstance * target, MTGAbility * a, int _tap = 0, int restrictions = 0, int _phase =
|
|
Constants::MTG_PHASE_UPKEEP,bool forcedestroy = false,bool next = true);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
APhaseActionGeneric * clone() const;
|
|
~APhaseActionGeneric();
|
|
|
|
};
|
|
|
|
//ABlink
|
|
class ABlink: public MTGAbility
|
|
{
|
|
public:
|
|
bool blinkueot;
|
|
bool blinkForSource;
|
|
bool blinkhand;
|
|
MTGCardInstance * Blinked;
|
|
bool resolved;
|
|
ABlink(int _id, MTGCardInstance * card, MTGCardInstance * _target,bool blinkueot=false,bool blinkForSource = false,bool blinkhand = false);
|
|
void Update(float dt);
|
|
void resolveBlink();
|
|
int resolve();
|
|
const char * getMenuText();
|
|
ABlink * clone() const;
|
|
~ABlink();
|
|
};
|
|
|
|
//blinkinstant
|
|
class ABlinkGeneric: public InstantAbility
|
|
{
|
|
public:
|
|
bool blinkueot;
|
|
bool blinkForSource;
|
|
bool blinkhand;
|
|
ABlink * ability;
|
|
ABlinkGeneric(int _id, MTGCardInstance * card, MTGCardInstance * _target,bool blinkueot=false,bool blinkForSource = false,bool blinkhand = false);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
ABlinkGeneric * clone() const;
|
|
~ABlinkGeneric();
|
|
|
|
};
|
|
|
|
/*
|
|
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)
|
|
{
|
|
cd.AddCard(library->cards[library->nb_cards - 1 - nbcards]);
|
|
nbcards++;
|
|
}
|
|
init = 1;
|
|
Render(dt);
|
|
}
|
|
cd.Update(dt);
|
|
|
|
// cd.CheckUserInput(dt);
|
|
}
|
|
}
|
|
|
|
void Render(float dt)
|
|
{
|
|
if (waitingForAnswer)
|
|
{
|
|
cd.Render();
|
|
}
|
|
}
|
|
|
|
int fireAbility()
|
|
{
|
|
source->tap();
|
|
MTGLibrary * library = game->currentlyActing()->game->library;
|
|
MTGHand * hand = game->currentlyActing()->game->hand;
|
|
MTGCardInstance * card = library->removeCard(tc->getNextCardTarget());
|
|
//library->shuffleTopToBottom(nbcards - 1);
|
|
hand->addCard(card);
|
|
init = 0;
|
|
return 1;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
return 1;
|
|
}
|
|
;
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AAladdinsLamp ::: cd : " << cd << " ; nbcards : " << nbcards << " ; init : " << init << " (";
|
|
return TargetAbility::toString(out) << ")";
|
|
}
|
|
AAladdinsLamp * clone() const
|
|
{
|
|
AAladdinsLamp * a = NEW AAladdinsLamp(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
// Armageddon Clock
|
|
class AArmageddonClock: public MTGAbility
|
|
{
|
|
public:
|
|
int counters;
|
|
ManaCost cost;
|
|
AArmageddonClock(int id, MTGCardInstance * _source) :
|
|
MTGAbility(id, _source)
|
|
{
|
|
counters = 0;
|
|
int _cost[] = { Constants::MTG_COLOR_ARTIFACT, 4 };
|
|
cost = ManaCost(_cost, 1);
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase)
|
|
{
|
|
if (newPhase == Constants::MTG_PHASE_UPKEEP && game->currentPlayer->game->inPlay->hasCard(source))
|
|
{
|
|
counters++;
|
|
}
|
|
else if (newPhase == Constants::MTG_PHASE_DRAW && counters > 0 && game->currentPlayer->game->inPlay->hasCard(source))
|
|
{ //End of upkeep = beginning of draw
|
|
GameObserver::GetInstance()->mLayers->stackLayer()->addDamage(source, GameObserver::GetInstance()->players[0],
|
|
counters);
|
|
GameObserver::GetInstance()->mLayers->stackLayer()->addDamage(source, GameObserver::GetInstance()->players[1],
|
|
counters);
|
|
}
|
|
}
|
|
}
|
|
int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL)
|
|
{
|
|
if (counters > 0 && _card == source && currentPhase == Constants::MTG_PHASE_UPKEEP)
|
|
{
|
|
if (game->currentlyActing()->getManaPool()->canAfford(&cost))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card)
|
|
{
|
|
if (!isReactingToClick(_card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(&cost);
|
|
counters--;
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AArmageddonClock ::: counters : " << counters << " ; cost : " << cost << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AArmageddonClock * clone() const
|
|
{
|
|
AArmageddonClock * a = NEW AArmageddonClock(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
// Clockwork Beast
|
|
class AClockworkBeast: public MTGAbility
|
|
{
|
|
public:
|
|
int counters;
|
|
ManaCost cost;
|
|
AClockworkBeast(int id, MTGCardInstance * _source) :
|
|
MTGAbility(id, _source)
|
|
{
|
|
counters = 7;
|
|
((MTGCardInstance *) target)->power += 7;
|
|
int _cost[] = { Constants::MTG_COLOR_ARTIFACT, 1 };
|
|
cost = ManaCost(_cost, 1);
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_COMBATEND)
|
|
{
|
|
if (((MTGCardInstance *) source)->isAttacker() || ((MTGCardInstance *) source)->isDefenser())
|
|
{
|
|
counters--;
|
|
((MTGCardInstance *) target)->power -= 1;
|
|
}
|
|
}
|
|
}
|
|
int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL)
|
|
{
|
|
if (counters < 7 && _card == source && currentPhase == Constants::MTG_PHASE_UPKEEP
|
|
&& game->currentPlayer->game->inPlay->hasCard(source))
|
|
{
|
|
if (game->currentlyActing()->getManaPool()->canAfford(&cost))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card)
|
|
{
|
|
if (!isReactingToClick(_card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(&cost);
|
|
counters++;
|
|
((MTGCardInstance *) target)->power++;
|
|
((MTGCardInstance *) target)->tap();
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AClockworkBeast ::: counters : " << counters << " ; cost : " << cost << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AClockworkBeast * clone() const
|
|
{
|
|
AClockworkBeast * a = NEW AClockworkBeast(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1102: Conservator
|
|
class AConservator: public MTGAbility
|
|
{
|
|
public:
|
|
int canprevent;
|
|
ManaCost cost;
|
|
AConservator(int _id, MTGCardInstance * _source) :
|
|
MTGAbility(_id, _source)
|
|
{
|
|
canprevent = 0;
|
|
int _cost[] = { Constants::MTG_COLOR_ARTIFACT, 2 };
|
|
cost = ManaCost(_cost, 1);
|
|
}
|
|
|
|
int alterDamage(Damage * damage)
|
|
{
|
|
if (canprevent && damage->target == source->controller())
|
|
{
|
|
if (damage->damage >= canprevent)
|
|
{
|
|
damage->damage -= canprevent;
|
|
canprevent = 0;
|
|
}
|
|
else
|
|
{
|
|
canprevent -= damage->damage;
|
|
damage->damage = 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
int alterDamage()
|
|
{
|
|
if (canprevent)
|
|
{
|
|
ActionStack * stack = game->mLayers->stackLayer();
|
|
for (int i = stack->mCount - 1; i >= 0; i--)
|
|
{
|
|
if (!canprevent) return 1;
|
|
Interruptible * current = ((Interruptible *) stack->mObjects[i]);
|
|
if (current->type == ACTION_DAMAGE && current->state == NOT_RESOLVED)
|
|
{
|
|
Damage * damage = (Damage *) current;
|
|
alterDamage(damage);
|
|
}
|
|
else if (current->type == ACTION_DAMAGES && current->state == NOT_RESOLVED)
|
|
{
|
|
DamageStack * damages = (DamageStack *) current;
|
|
for (int j = damages->mCount - 1; j >= 0; j--)
|
|
{
|
|
alterDamage(((Damage *) damages->mObjects[j]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
alterDamage();
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL)
|
|
{
|
|
if (_card == source && game->currentlyActing()->game->inPlay->hasCard(source) && !_card->isTapped())
|
|
{
|
|
if (game->currentlyActing()->getManaPool()->canAfford(&cost))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card)
|
|
{
|
|
if (!isReactingToClick(_card)) return 0;
|
|
game->currentlyActing()->getManaPool()->pay(&cost);
|
|
source->tap();
|
|
canprevent = 2;
|
|
alterDamage();
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AConservator ::: canprevent : " << canprevent << " ; cost : " << cost << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AConservator * clone() const
|
|
{
|
|
AConservator * a = NEW AConservator(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1345 Farmstead
|
|
class AFarmstead: public ActivatedAbility
|
|
{
|
|
public:
|
|
int usedThisTurn;
|
|
AFarmstead(int _id, MTGCardInstance * source, MTGCardInstance * _target) :
|
|
ActivatedAbility(_id, source, 0, 1, 0)
|
|
{
|
|
int _cost[] = { Constants::MTG_COLOR_WHITE, 2 };
|
|
cost = NEW ManaCost(_cost, 1);
|
|
target = _target;
|
|
usedThisTurn = 0;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase != Constants::MTG_PHASE_UPKEEP)
|
|
{
|
|
usedThisTurn = 0;
|
|
}
|
|
ActivatedAbility::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL)
|
|
{
|
|
if (!ActivatedAbility::isReactingToClick(card, mana)) return 0;
|
|
if (currentPhase != Constants::MTG_PHASE_UPKEEP) return 0;
|
|
if (usedThisTurn) return 0;
|
|
return 1;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
source->controller()->life++;
|
|
usedThisTurn = 1;
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AFarmstead ::: usedThisTurn : " << usedThisTurn << " (";
|
|
return ActivatedAbility::toString(out) << ")";
|
|
}
|
|
AFarmstead * clone() const
|
|
{
|
|
AFarmstead * a = NEW AFarmstead(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1112 Howling Mine
|
|
class AHowlingMine: public MTGAbility
|
|
{
|
|
public:
|
|
AHowlingMine(int _id, MTGCardInstance * _source) :
|
|
MTGAbility(_id, _source)
|
|
{
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_DRAW && !source->isTapped())
|
|
{
|
|
game->mLayers->stackLayer()->addDraw(game->currentPlayer);
|
|
}
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AHowlingMine ::: (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AHowlingMine * clone() const
|
|
{
|
|
AHowlingMine * a = NEW AHowlingMine(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Kjeldoran Frostbeast
|
|
class AKjeldoranFrostbeast: public MTGAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * opponents[20];
|
|
int nbOpponents;
|
|
AKjeldoranFrostbeast(int _id, MTGCardInstance * _source) :
|
|
MTGAbility(_id, _source)
|
|
{
|
|
nbOpponents = 0;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase)
|
|
{
|
|
if (newPhase == Constants::MTG_PHASE_COMBATEND)
|
|
{
|
|
nbOpponents = 0;
|
|
MTGCardInstance * opponent = source->getNextOpponent();
|
|
while (opponent && !opponent->hasSubtype("wall"))
|
|
{
|
|
opponents[nbOpponents] = opponent;
|
|
nbOpponents++;
|
|
opponent = source->getNextOpponent(opponent);
|
|
}
|
|
if (source->isInPlay())
|
|
{
|
|
for (int i = 0; i < nbOpponents; i++)
|
|
{
|
|
opponents[i]->destroy();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int testDestroy()
|
|
{
|
|
if (!game->isInPlay(source) && currentPhase != Constants::MTG_PHASE_UNTAP)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return MTGAbility::testDestroy();
|
|
}
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AKjeldoranFrostbeast ::: opponents : " << opponents << " ; nbOpponents : " << nbOpponents << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AKjeldoranFrostbeast * clone() const
|
|
{
|
|
AKjeldoranFrostbeast * a = NEW AKjeldoranFrostbeast(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Living Artifact
|
|
class ALivingArtifact: public MTGAbility
|
|
{
|
|
public:
|
|
int usedThisTurn;
|
|
int counters;
|
|
Damage * latest;
|
|
ALivingArtifact(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
MTGAbility(_id, _source, _target)
|
|
{
|
|
usedThisTurn = 0;
|
|
counters = 0;
|
|
latest = NULL;
|
|
}
|
|
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
WEventDamage * e = dynamic_cast<WEventDamage *> (event);
|
|
if (!e) return 0;
|
|
Player * p = dynamic_cast<Player *> (e->damage->target);
|
|
if (!p) return 0;
|
|
if (p != source->controller()) return 0;
|
|
counters += e->damage->damage;
|
|
return 1; //is this meant to return 0 or 1?
|
|
}
|
|
|
|
int isReactingtoclick(MTGCardInstance * card, ManaCost * mana = NULL)
|
|
{
|
|
if (currentPhase == Constants::MTG_PHASE_UPKEEP && card == source && game->currentPlayer == source->controller()
|
|
&& counters && !usedThisTurn)
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * card)
|
|
{
|
|
source->controller()->life += 1;
|
|
counters--;
|
|
usedThisTurn = 1;
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ALivingArtifact ::: usedThisTurn : " << usedThisTurn << " ; counters : " << counters << " ; latest : " << latest
|
|
<< " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
ALivingArtifact * clone() const
|
|
{
|
|
ALivingArtifact * a = NEW ALivingArtifact(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1143 Animate Dead
|
|
class AAnimateDead: public MTGAbility
|
|
{
|
|
public:
|
|
AAnimateDead(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
MTGAbility(_id, _source, _target)
|
|
{
|
|
MTGCardInstance * card = _target;
|
|
|
|
//Put the card in play again, with all its abilities !
|
|
//AbilityFactory af;
|
|
MTGCardInstance * copy = source->controller()->game->putInZone(card, _target->controller()->game->graveyard,
|
|
source->controller()->game->temp);
|
|
Spell * spell = NEW Spell(copy);
|
|
spell->resolve();
|
|
target = spell->source;
|
|
card = spell->source;
|
|
card->power--;
|
|
card->life = card->toughness;
|
|
delete spell;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
MTGCardInstance * card = (MTGCardInstance *) target;
|
|
card->power++;
|
|
card->controller()->game->putInZone(card, card->controller()->game->inPlay, card->owner->game->graveyard);
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AAnimateDead ::: (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AAnimateDead * clone() const
|
|
{
|
|
AAnimateDead * a = NEW AAnimateDead(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1159 Erg Raiders
|
|
class AErgRaiders: public MTGAbility
|
|
{
|
|
public:
|
|
int attackedThisTurn;
|
|
AErgRaiders(int _id, MTGCardInstance * _source) :
|
|
MTGAbility(_id, _source)
|
|
{
|
|
attackedThisTurn = 1;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase)
|
|
{
|
|
Player * controller = source->controller();
|
|
if (newPhase == Constants::MTG_PHASE_COMBATDAMAGE && game->currentPlayer == controller)
|
|
{
|
|
if (source->isAttacker())
|
|
{
|
|
attackedThisTurn = 1;
|
|
}
|
|
}
|
|
else if (newPhase == Constants::MTG_PHASE_UNTAP)
|
|
{
|
|
if (game->currentPlayer != controller && !attackedThisTurn)
|
|
{
|
|
game->mLayers->stackLayer()->addDamage(source, controller, 2);
|
|
}
|
|
else if (game->currentPlayer == controller)
|
|
{
|
|
attackedThisTurn = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
AErgRaiders * clone() const
|
|
{
|
|
AErgRaiders * a = NEW AErgRaiders(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Fastbond
|
|
class AFastbond: public TriggeredAbility
|
|
{
|
|
public:
|
|
int alreadyPlayedALand;
|
|
int previous;
|
|
AFastbond(int _id, MTGCardInstance * card) :
|
|
TriggeredAbility(_id, card)
|
|
{
|
|
alreadyPlayedALand = 0;
|
|
if (source->controller()->landsPlayerCanStillPlay == 0)
|
|
{
|
|
alreadyPlayedALand = 1;
|
|
source->controller()->landsPlayerCanStillPlay += 1;
|
|
source->controller()->canPutLandsIntoPlay = true;
|
|
|
|
}
|
|
previous = source->controller()->landsPlayerCanStillPlay;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UNTAP)
|
|
{
|
|
alreadyPlayedALand = 0;
|
|
}
|
|
TriggeredAbility::Update(dt);
|
|
}
|
|
|
|
int trigger()
|
|
{
|
|
if (source->controller()->landsPlayerCanStillPlay == 0 && previous >= 1)
|
|
{
|
|
previous = 0;
|
|
source->controller()->canPutLandsIntoPlay = true;
|
|
source->controller()->landsPlayerCanStillPlay += 1;
|
|
if (alreadyPlayedALand) return 1;
|
|
alreadyPlayedALand = 1;
|
|
return 0;
|
|
}
|
|
previous = source->controller()->landsPlayerCanStillPlay;
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
game->mLayers->stackLayer()->addDamage(source, source->controller(), 1);
|
|
game->mLayers->stackLayer()->resolve();
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AFastbond ::: alreadyPlayedALand : " << alreadyPlayedALand << " ; previous : " << previous << " (";
|
|
return TriggeredAbility::toString(out) << ")";
|
|
}
|
|
AFastbond * clone() const
|
|
{
|
|
AFastbond * a = NEW AFastbond(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1165 Hypnotic Specter
|
|
class AHypnoticSpecter: public MTGAbility
|
|
{
|
|
public:
|
|
|
|
AHypnoticSpecter(int _id, MTGCardInstance * _source) :
|
|
MTGAbility(_id, _source)
|
|
{
|
|
}
|
|
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
WEventDamage * e = dynamic_cast<WEventDamage *> (event);
|
|
if (!e) return 0;
|
|
if (e->damage->source != source) return 0;
|
|
Player * p = dynamic_cast<Player *> (e->damage->target);
|
|
if (!p) return 0;
|
|
p->game->discardRandom(p->game->hand, source);
|
|
return 1; //is this meant to return 0 or 1?
|
|
}
|
|
|
|
AHypnoticSpecter * clone() const
|
|
{
|
|
AHypnoticSpecter * a = NEW AHypnoticSpecter(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1117 Jandor's Ring
|
|
class AJandorsRing: public ActivatedAbility
|
|
{
|
|
public:
|
|
AJandorsRing(int _id, MTGCardInstance * _source) :
|
|
ActivatedAbility(_id, _source, NEW ManaCost())
|
|
{
|
|
cost->add(Constants::MTG_COLOR_ARTIFACT, 2);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL)
|
|
{
|
|
if (!source->controller()->game->hand->hasCard(source->controller()->game->library->lastCardDrawn)) return 0;
|
|
return ActivatedAbility::isReactingToClick(card, mana);
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
source->controller()->game->putInGraveyard(source->controller()->game->library->lastCardDrawn);
|
|
game->mLayers->stackLayer()->addDraw(source->controller());
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AJandorsRing ::: (";
|
|
return ActivatedAbility::toString(out) << ")";
|
|
}
|
|
AJandorsRing * clone() const
|
|
{
|
|
AJandorsRing * a = NEW AJandorsRing(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Kudzu.
|
|
//What happens when there are no targets ???
|
|
class AKudzu: public TargetAbility
|
|
{
|
|
public:
|
|
AKudzu(int _id, MTGCardInstance * card, MTGCardInstance * _target) :
|
|
TargetAbility(_id, card, NEW TypeTargetChooser("land", card))
|
|
{
|
|
tc->toggleTarget(_target);
|
|
target = _target;
|
|
}
|
|
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
if (WEventCardTap* wect = dynamic_cast<WEventCardTap*>(event))
|
|
{
|
|
if (wect->before == false && wect->after == true && wect->card == target)
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (!_target->isInPlay()) return 0;
|
|
target = _target->controller()->game->putInGraveyard(_target);
|
|
reactToClick(source);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL)
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (card == source && (!_target || !_target->isInPlay()))
|
|
{
|
|
DebugTrace("Kudzu Reacts to click !");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
target = tc->getNextCardTarget();
|
|
source->target = (MTGCardInstance *) target;
|
|
return 1;
|
|
}
|
|
|
|
int testDestroy()
|
|
{
|
|
int stillLandsInPlay = 0;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
if (game->players[i]->game->inPlay->hasType("Land")) stillLandsInPlay = 1;
|
|
}
|
|
if (!stillLandsInPlay)
|
|
{
|
|
source->controller()->game->putInGraveyard(source);
|
|
return 1;
|
|
}
|
|
|
|
if (!game->isInPlay(source))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
AKudzu * clone() const
|
|
{
|
|
AKudzu * a = NEW AKudzu(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1172 Pestilence
|
|
class APestilence: public ActivatedAbility
|
|
{
|
|
public:
|
|
APestilence(int _id, MTGCardInstance * card) :
|
|
ActivatedAbility(_id, card, NEW ManaCost(), 0, 0)
|
|
{
|
|
cost->add(Constants::MTG_COLOR_BLACK, 1);
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_ENDOFTURN)
|
|
{
|
|
if (!game->players[0]->game->inPlay->hasType("creature") && !game->players[1]->game->inPlay->hasType("creature"))
|
|
{
|
|
source->controller()->game->putInGraveyard(source);
|
|
}
|
|
}
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
MTGInPlay * inplay = game->players[i]->game->inPlay;
|
|
for (int j = inplay->nb_cards - 1; j >= 0; j--)
|
|
{
|
|
if (inplay->cards[j]->isCreature()) game->mLayers->stackLayer()->addDamage(source, inplay->cards[j], 1);
|
|
}
|
|
game->mLayers->stackLayer()->addDamage(source, game->players[i], 1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "APestilence ::: (";
|
|
return ActivatedAbility::toString(out) << ")";
|
|
}
|
|
APestilence * clone() const
|
|
{
|
|
APestilence * a = NEW APestilence(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Power Leak
|
|
class APowerLeak: public TriggeredAbility
|
|
{
|
|
public:
|
|
int damagesToDealThisTurn;
|
|
ManaCost cost;
|
|
APowerLeak(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
TriggeredAbility(_id, _source, _target)
|
|
{
|
|
cost.add(Constants::MTG_COLOR_ARTIFACT, 1);
|
|
damagesToDealThisTurn = 0;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_UPKEEP && _target->controller() == game->currentPlayer)
|
|
{
|
|
damagesToDealThisTurn = 2;
|
|
}
|
|
TriggeredAbility::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL)
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (damagesToDealThisTurn && currentPhase == Constants::MTG_PHASE_UPKEEP && card == source && _target->controller()
|
|
== game->currentPlayer)
|
|
{
|
|
if (game->currentPlayer->getManaPool()->canAfford(&cost)) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToclick(MTGCardInstance * card)
|
|
{
|
|
game->currentPlayer->getManaPool()->pay(&cost);
|
|
damagesToDealThisTurn--;
|
|
return 1;
|
|
}
|
|
|
|
int trigger()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_DRAW && _target->controller() == game->currentPlayer)
|
|
{
|
|
if (damagesToDealThisTurn) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
game->mLayers->stackLayer()->addDamage(source, _target->controller(), damagesToDealThisTurn);
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "APowerLeak ::: damagesToDealThisTurn : " << damagesToDealThisTurn << " ; cost : " << cost << " (";
|
|
return TriggeredAbility::toString(out) << ")";
|
|
}
|
|
APowerLeak * clone() const
|
|
{
|
|
APowerLeak * a = NEW APowerLeak(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1176 Sacrifice
|
|
class ASacrifice: public InstantAbility
|
|
{
|
|
public:
|
|
ASacrifice(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
InstantAbility(_id, _source)
|
|
{
|
|
target = _target;
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (_target->isInPlay())
|
|
{
|
|
game->currentlyActing()->game->putInGraveyard(_target);
|
|
int x = _target->getManaCost()->getConvertedCost();
|
|
game->currentlyActing()->getManaPool()->add(Constants::MTG_COLOR_BLACK, x);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ASacrifice ::: (";
|
|
return InstantAbility::toString(out) << ")";
|
|
}
|
|
ASacrifice * clone() const
|
|
{
|
|
ASacrifice * a = NEW ASacrifice(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1178 Scavenging Ghoul
|
|
class AScavengingGhoul: public MTGAbility
|
|
{
|
|
public:
|
|
int counters;
|
|
AScavengingGhoul(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
MTGAbility(_id, _source, _target)
|
|
{
|
|
counters = 0;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
//TODO
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL)
|
|
{
|
|
if (counters > 0 && _card == source && game->currentlyActing()->game->inPlay->hasCard(source))
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * _card)
|
|
{
|
|
if (!isReactingToClick(_card)) return 0;
|
|
counters--;
|
|
source->regenerate();
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AScavengingGhoul ::: counters : " << counters << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AScavengingGhoul * clone() const
|
|
{
|
|
AScavengingGhoul * a = NEW AScavengingGhoul(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1235 Aspect of Wolf
|
|
class AAspectOfWolf: public ListMaintainerAbility
|
|
{
|
|
public:
|
|
int color;
|
|
AAspectOfWolf(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
ListMaintainerAbility(_id, _source, _target)
|
|
{
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card)
|
|
{
|
|
|
|
if (card->controller() == source->controller() && card->hasType("forest") && game->isInPlay(card)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card)
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
int size = cards.size();
|
|
if (size % 2 == 0)
|
|
{
|
|
_target->power += 1;
|
|
}
|
|
else
|
|
{
|
|
_target->addToToughness(1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card)
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
int size = cards.size();
|
|
if (size % 2 == 1)
|
|
{
|
|
_target->power -= 1;
|
|
}
|
|
else
|
|
{
|
|
_target->addToToughness(-1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AAspectOfWolf ::: color : " << color << " (";
|
|
return ListMaintainerAbility::toString(out) << ")";
|
|
}
|
|
AAspectOfWolf * clone() const
|
|
{
|
|
AAspectOfWolf * a = NEW AAspectOfWolf(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1284 Dragon Whelp
|
|
class ADragonWhelp: public APowerToughnessModifierUntilEndOfTurn
|
|
{
|
|
public:
|
|
ADragonWhelp(int id, MTGCardInstance * card) :
|
|
APowerToughnessModifierUntilEndOfTurn(id, card, card, NEW WParsedPT(1, 0), NEW ManaCost())
|
|
{
|
|
cost->add(Constants::MTG_COLOR_RED, 1);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL)
|
|
{
|
|
if (!ActivatedAbility::isReactingToClick(card, mana)) return 0;
|
|
return (!maxcounters || (counters < maxcounters));
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_AFTER_EOT && counters > 3)
|
|
{
|
|
source->controller()->game->putInGraveyard(source);
|
|
}
|
|
APowerToughnessModifierUntilEndOfTurn::Update(dt);
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ADragonWhelp ::: (";
|
|
return APowerToughnessModifierUntilEndOfTurn::toString(out) << ")";
|
|
}
|
|
ADragonWhelp * clone() const
|
|
{
|
|
ADragonWhelp * a = NEW ADragonWhelp(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1288 EarthBind
|
|
class AEarthbind: public ABasicAbilityModifier
|
|
{
|
|
public:
|
|
AEarthbind(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
ABasicAbilityModifier(_id, _source, _target, Constants::FLYING, 0)
|
|
{
|
|
if (value_before_modification)
|
|
{
|
|
Damageable * _target = (Damageable *) target;
|
|
game->mLayers->stackLayer()->addDamage(source, _target, 2);
|
|
}
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AEarthbind ::: (";
|
|
return ABasicAbilityModifier::toString(out) << ")";
|
|
}
|
|
AEarthbind * clone() const
|
|
{
|
|
AEarthbind * a = NEW AEarthbind(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1291 Fireball
|
|
class AFireball: public InstantAbility
|
|
{
|
|
public:
|
|
AFireball(int _id, MTGCardInstance * card, Spell * spell, int x) :
|
|
InstantAbility(_id, card)
|
|
{
|
|
int nbtargets = spell->getNbTargets();
|
|
int totaldamage = x + 1 - nbtargets;
|
|
int individualdamage = 0;
|
|
if (nbtargets) individualdamage = totaldamage / nbtargets;
|
|
Damageable * _target = spell->getNextDamageableTarget();
|
|
while (_target)
|
|
{
|
|
game->mLayers->stackLayer()->addDamage(source, _target, individualdamage);
|
|
_target = spell->getNextDamageableTarget(_target);
|
|
}
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AFireball ::: (";
|
|
return InstantAbility::toString(out) << ")";
|
|
}
|
|
AFireball * clone() const
|
|
{
|
|
AFireball * a = NEW AFireball(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//1351 Island Sanctuary
|
|
class AIslandSanctuary: public MTGAbility
|
|
{
|
|
public:
|
|
int initThisTurn;
|
|
AIslandSanctuary(int _id, MTGCardInstance * _source) :
|
|
MTGAbility(_id, _source)
|
|
{
|
|
initThisTurn = 0;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (currentPhase == Constants::MTG_PHASE_UNTAP && game->currentPlayer == source->controller()) initThisTurn = 0;
|
|
|
|
if (initThisTurn && currentPhase == Constants::MTG_PHASE_COMBATATTACKERS && game->currentPlayer != source->controller())
|
|
{
|
|
MTGGameZone * zone = game->currentPlayer->game->inPlay;
|
|
for (int i = 0; i < zone->nb_cards; i++)
|
|
{
|
|
MTGCardInstance * card = zone->cards[i];
|
|
if (card->isAttacker() && !card->basicAbilities[Constants::FLYING] && !card->basicAbilities[Constants::ISLANDWALK]) source->toggleAttacker();
|
|
}
|
|
}
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL)
|
|
{
|
|
if (card == source && game->currentPlayer == card->controller() && currentPhase == Constants::MTG_PHASE_DRAW)
|
|
{
|
|
Interruptible * action = game->mLayers->stackLayer()->getAt(-1);
|
|
if (action->type == ACTION_DRAW) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int reactToClick(MTGCardInstance * card)
|
|
{
|
|
if (!isReactingToClick(card)) return 0;
|
|
game->mLayers->stackLayer()->Remove(game->mLayers->stackLayer()->getAt(-1));
|
|
initThisTurn = 1;
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AIslandSanctuary ::: initThisTurn : " << initThisTurn << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
AIslandSanctuary * clone() const
|
|
{
|
|
AIslandSanctuary * a = NEW AIslandSanctuary(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Stasis
|
|
class AStasis: public ActivatedAbility
|
|
{
|
|
public:
|
|
int paidThisTurn;
|
|
AStasis(int _id, MTGCardInstance * card) :
|
|
ActivatedAbility(_id, card, NEW ManaCost(), 1, 0)
|
|
{
|
|
paidThisTurn = 1;
|
|
cost->add(Constants::MTG_COLOR_BLUE, 1);
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
//Upkeep Cost
|
|
if (newPhase != currentPhase)
|
|
{
|
|
if (newPhase == Constants::MTG_PHASE_UPKEEP)
|
|
{
|
|
paidThisTurn = 0;
|
|
}
|
|
else if (!paidThisTurn && newPhase > Constants::MTG_PHASE_UPKEEP && game->currentPlayer == source->controller())
|
|
{
|
|
game->currentPlayer->game->putInGraveyard(source);
|
|
paidThisTurn = 1;
|
|
}
|
|
}
|
|
//Stasis Effect
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
game->phaseRing->removePhase(Constants::MTG_PHASE_UNTAP, game->players[i]);
|
|
}
|
|
|
|
//Parent Class Method Call
|
|
ActivatedAbility::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL)
|
|
{
|
|
return (!paidThisTurn && currentPhase == Constants::MTG_PHASE_UPKEEP && ActivatedAbility::isReactingToClick(card, mana));
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
paidThisTurn = 1;
|
|
return 1;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
game->phaseRing->addPhaseBefore(Constants::MTG_PHASE_UNTAP, game->players[i], Constants::MTG_PHASE_UPKEEP,
|
|
game->players[i]);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AStasis ::: paidThisTurn : " << paidThisTurn << " (";
|
|
return ActivatedAbility::toString(out) << ")";
|
|
}
|
|
AStasis * clone() const
|
|
{
|
|
AStasis * a = NEW AStasis(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//--------------Addon Abra------------------
|
|
|
|
//Basilik --> needs to be made more generic to avoid duplicate (also something like if opponent=type then ...)
|
|
class ABasilik: public MTGAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * opponents[20];
|
|
int nbOpponents;
|
|
ABasilik(int _id, MTGCardInstance * _source) :
|
|
MTGAbility(_id, _source)
|
|
{
|
|
nbOpponents = 0;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase)
|
|
{
|
|
if (newPhase == Constants::MTG_PHASE_COMBATDAMAGE)
|
|
{
|
|
nbOpponents = 0;
|
|
MTGCardInstance * opponent = source->getNextOpponent();
|
|
while (opponent)
|
|
{
|
|
opponents[nbOpponents] = opponent;
|
|
nbOpponents++;
|
|
opponent = source->getNextOpponent(opponent);
|
|
}
|
|
}
|
|
else if (newPhase == Constants::MTG_PHASE_COMBATEND)
|
|
{
|
|
for (int i = 0; i < nbOpponents; i++)
|
|
{
|
|
game->mLayers->stackLayer()->addPutInGraveyard(opponents[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ABasilik ::: opponents : " << opponents << " ; nbOpponents : " << nbOpponents << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
ABasilik * clone() const
|
|
{
|
|
ABasilik * a = NEW ABasilik(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Lavaborn - quick and very dirty ;) copy of ALifezonelink but without the multiplier.
|
|
class ALavaborn: public MTGAbility
|
|
{
|
|
public:
|
|
int phase;
|
|
int condition;
|
|
int life;
|
|
int controller;
|
|
int nbcards;
|
|
MTGGameZone * zone;
|
|
ALavaborn(int _id, MTGCardInstance * card, int _phase, int _condition, int _life, int _controller = 0, MTGGameZone * _zone =
|
|
NULL) :
|
|
MTGAbility(_id, card)
|
|
{
|
|
phase = _phase;
|
|
condition = _condition;
|
|
controller = _controller;
|
|
life = _life;
|
|
zone = _zone;
|
|
if (zone == NULL)
|
|
{
|
|
if (controller)
|
|
{
|
|
zone = game->currentPlayer->game->hand;
|
|
}
|
|
else
|
|
{
|
|
zone = game->opponent()->game->hand;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && newPhase == phase)
|
|
{
|
|
if ((controller && game->currentPlayer == source->controller()) || (!controller && game->currentPlayer
|
|
!= source->controller()))
|
|
{
|
|
if ((condition < 0 && zone->nb_cards < -condition) || (condition > 0 && zone->nb_cards > condition))
|
|
{
|
|
int diff = zone->nb_cards - condition;
|
|
if (condition < 0) diff = -condition - zone->nb_cards;
|
|
if (life > 0)
|
|
{
|
|
game->currentPlayer->life += life;
|
|
}
|
|
else
|
|
{
|
|
game->mLayers->stackLayer()->addDamage(source, game->currentPlayer, -life);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ALavaborn ::: phase : " << phase << " ; condition : " << condition << " ; life : " << life << " ; controller : "
|
|
<< controller << " ; nbcards : " << nbcards << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
|
|
ALavaborn * clone() const
|
|
{
|
|
ALavaborn * a = NEW ALavaborn(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
|
|
};
|
|
|
|
//Generic Millstone
|
|
class AADepleter: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
WParsedInt * nbcards;
|
|
WParsedInt * RefreshedNbcards;
|
|
string nbcardsStr;
|
|
|
|
AADepleter(int _id, MTGCardInstance * card, Targetable * _target, WParsedInt * nbcards,string nbcardsStr, ManaCost * _cost = NULL, int _tap = 0,
|
|
int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AADepleter * clone() const;
|
|
~AADepleter();
|
|
};
|
|
|
|
//Shuffle
|
|
class AAShuffle: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
AAShuffle(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int _tap = 0, int who =
|
|
TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AAShuffle * clone() const;
|
|
};
|
|
|
|
//only 1 spell
|
|
class AAOnlyOne: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
AAOnlyOne(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int _tap = 0, int who =
|
|
TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AAOnlyOne * clone() const;
|
|
};
|
|
|
|
//nospells
|
|
class AANoSpells: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
AANoSpells(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int _tap = 0, int who =
|
|
TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AANoSpells * clone() const;
|
|
};
|
|
|
|
//NoCreature
|
|
class AANoCreatures: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
AANoCreatures(int _id, MTGCardInstance * card, Targetable * _target, ManaCost * _cost = NULL, int _tap = 0, int who =
|
|
TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AANoCreatures * clone() const;
|
|
};
|
|
|
|
//Random Discard
|
|
class AARandomDiscarder: public ActivatedAbilityTP
|
|
{
|
|
public:
|
|
WParsedInt * nbcards;
|
|
WParsedInt * RefreshedNbcards;
|
|
string nbcardsStr;
|
|
|
|
AARandomDiscarder(int _id, MTGCardInstance * card, Targetable * _target, WParsedInt * nbcards,string nbcardsStr, ManaCost * _cost = NULL,
|
|
int _tap = 0, int who = TargetChooser::UNSET);
|
|
int resolve();
|
|
const char * getMenuText();
|
|
AARandomDiscarder * clone() const;
|
|
~AARandomDiscarder();
|
|
};
|
|
|
|
//Minion of Leshrac
|
|
class AMinionofLeshrac: public TargetAbility
|
|
{
|
|
public:
|
|
int paidThisTurn;
|
|
AMinionofLeshrac(int _id, MTGCardInstance * source) :
|
|
TargetAbility(_id, source, NEW CreatureTargetChooser(), 0, 1, 0)
|
|
{
|
|
paidThisTurn = 1;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
if (newPhase != currentPhase && source->controller() == game->currentPlayer)
|
|
{
|
|
if (newPhase == Constants::MTG_PHASE_UNTAP)
|
|
{
|
|
paidThisTurn = 0;
|
|
}
|
|
else if (newPhase == Constants::MTG_PHASE_UPKEEP + 1 && !paidThisTurn)
|
|
{
|
|
game->mLayers->stackLayer()->addDamage(source, source->controller(), 5);
|
|
source->tap();
|
|
}
|
|
}
|
|
TargetAbility::Update(dt);
|
|
}
|
|
|
|
int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL)
|
|
{
|
|
if (currentPhase != Constants::MTG_PHASE_UPKEEP || paidThisTurn) return 0;
|
|
return TargetAbility::isReactingToClick(card, mana);
|
|
}
|
|
|
|
int resolve()
|
|
{
|
|
MTGCardInstance * card = tc->getNextCardTarget();
|
|
if (card && card != source && card->controller() == source->controller())
|
|
{
|
|
card->controller()->game->putInGraveyard(card);
|
|
paidThisTurn = 1;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AMinionofLeshrac ::: paidThisTurn : " << paidThisTurn << " (";
|
|
return TargetAbility::toString(out) << ")";
|
|
}
|
|
|
|
AMinionofLeshrac * clone() const
|
|
{
|
|
AMinionofLeshrac * a = NEW AMinionofLeshrac(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Rampage ability
|
|
class ARampageAbility: public MTGAbility
|
|
{
|
|
public:
|
|
int nbOpponents;
|
|
int PowerModifier;
|
|
int ToughnessModifier;
|
|
int MaxOpponent;
|
|
|
|
ARampageAbility(int _id, MTGCardInstance * _source, int _PowerModifier, int _ToughnessModifier, int _MaxOpponent) :
|
|
MTGAbility(_id, _source)
|
|
{
|
|
PowerModifier = _PowerModifier;
|
|
ToughnessModifier = _ToughnessModifier;
|
|
MaxOpponent = _MaxOpponent;
|
|
nbOpponents = 0;
|
|
}
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
if (dynamic_cast<WEventBlockersChosen*> (event))
|
|
{
|
|
nbOpponents = source->blockers.size();
|
|
if (nbOpponents <= MaxOpponent) return 0;
|
|
source->power += PowerModifier * (nbOpponents - MaxOpponent);
|
|
source->addToToughness(ToughnessModifier * (nbOpponents - MaxOpponent));
|
|
}
|
|
else if (WEventPhaseChange* pe = dynamic_cast<WEventPhaseChange*>(event))
|
|
{
|
|
if (Constants::MTG_PHASE_AFTER_EOT == pe->to->id && nbOpponents > MaxOpponent)
|
|
{
|
|
source->power -= PowerModifier * (nbOpponents - MaxOpponent);
|
|
source->addToToughness(-ToughnessModifier * (nbOpponents - MaxOpponent));
|
|
nbOpponents = 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
ARampageAbility * clone() const
|
|
{
|
|
ARampageAbility * a = NEW ARampageAbility(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//flanking ability
|
|
class AFlankerAbility: public MTGAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * opponents[20];
|
|
int nbOpponents;
|
|
AFlankerAbility(int _id, MTGCardInstance * _source) :
|
|
MTGAbility(_id, _source)
|
|
{
|
|
nbOpponents = 0;
|
|
}
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
if (dynamic_cast<WEventBlockersChosen*> (event))
|
|
{
|
|
nbOpponents = 0;
|
|
MTGCardInstance * opponent = source->getNextOpponent();
|
|
while (opponent && !opponent->has(Constants::FLANKING) && game->currentlyActing() == source->controller()->opponent())
|
|
{
|
|
opponents[nbOpponents] = opponent;
|
|
nbOpponents++;
|
|
opponent = source->getNextOpponent(opponent);
|
|
}
|
|
for (int i = 0; i < nbOpponents; i++)
|
|
{
|
|
opponents[i]->power -= 1;
|
|
opponents[i]->addToToughness(-1);
|
|
opponents[i]->flanked += 1;
|
|
if (opponents[i]->life == 0)
|
|
{
|
|
opponents[i]->setPower(0);
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AFlankerAbility ::: opponents : " << opponents << " ; nbOpponents : " << nbOpponents << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
|
|
AFlankerAbility * clone() const
|
|
{
|
|
AFlankerAbility * a = NEW AFlankerAbility(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Bushido ability
|
|
class ABushidoAbility: public MTGAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * opponents[20];
|
|
int nbOpponents;
|
|
int PowerModifier;
|
|
int ToughnessModifier;
|
|
|
|
ABushidoAbility(int _id, MTGCardInstance * _source, int _PowerModifier, int _ToughnessModifier) :
|
|
MTGAbility(_id, _source)
|
|
{
|
|
PowerModifier = _PowerModifier;
|
|
ToughnessModifier = _ToughnessModifier;
|
|
nbOpponents = 0;
|
|
}
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
if (dynamic_cast<WEventBlockersChosen*> (event))
|
|
{
|
|
MTGCardInstance * opponent = source->getNextOpponent();
|
|
if (!opponent) return 0;
|
|
source->power += PowerModifier;
|
|
source->addToToughness(ToughnessModifier);
|
|
while (opponent)
|
|
{
|
|
opponents[nbOpponents] = opponent;
|
|
nbOpponents++;
|
|
opponent = source->getNextOpponent(opponent);
|
|
}
|
|
}
|
|
else if (WEventPhaseChange* pe = dynamic_cast<WEventPhaseChange*>(event))
|
|
{
|
|
if (Constants::MTG_PHASE_AFTER_EOT == pe->to->id && nbOpponents)
|
|
{
|
|
source->power -= PowerModifier;
|
|
source->addToToughness(-ToughnessModifier);
|
|
nbOpponents = 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "ABushidoAbility ::: opponents : " << opponents << " ; nbOpponents : " << nbOpponents << " (";
|
|
return MTGAbility::toString(out) << ")";
|
|
}
|
|
|
|
ABushidoAbility * clone() const
|
|
{
|
|
ABushidoAbility * a = NEW ABushidoAbility(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//A Spirit Link Ability
|
|
class ASpiritLinkAbility: public MTGAbility
|
|
{
|
|
public:
|
|
MTGCardInstance * source;
|
|
bool combatonly;
|
|
ASpiritLinkAbility(int _id, MTGCardInstance * _source,bool combatonly = false) :
|
|
MTGAbility(_id, _source),source(_source),combatonly(combatonly)
|
|
{
|
|
}
|
|
int receiveEvent(WEvent * event)
|
|
{
|
|
if (event->type == WEvent::DAMAGE)
|
|
{
|
|
WEventDamage * e = (WEventDamage *) event;
|
|
Damage * d = e->damage;
|
|
if (combatonly == true && e->damage->typeOfDamage != DAMAGE_COMBAT)
|
|
return 0;
|
|
MTGCardInstance * card = d->source;
|
|
if (d->damage > 0 && card && (card == source || card == source->target))
|
|
{
|
|
source->owner->life += d->damage;
|
|
source->owner->thatmuch = d->damage;
|
|
WEvent * lifed = NULL;
|
|
lifed = NEW WEventLife(source->owner,d->damage);
|
|
GameObserver * game = GameObserver::GetInstance();
|
|
game->receiveEvent(lifed);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
ASpiritLinkAbility * clone() const
|
|
{
|
|
ASpiritLinkAbility * a = NEW ASpiritLinkAbility(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Instant Steal control of a target
|
|
class AInstantControlSteal: public InstantAbility
|
|
{
|
|
public:
|
|
Player * TrueController;
|
|
Player * TheftController;
|
|
AInstantControlSteal(int _id, MTGCardInstance * _source, MTGCardInstance * _target) :
|
|
InstantAbility(_id, _source, _target)
|
|
{
|
|
TrueController = _target->controller();
|
|
TheftController = source->controller();
|
|
MTGCardInstance * copy = _target->changeController(TheftController);
|
|
target = copy;
|
|
source->target = copy;
|
|
copy->summoningSickness = 0;
|
|
}
|
|
|
|
int destroy()
|
|
{
|
|
MTGCardInstance * _target = (MTGCardInstance *) target;
|
|
if (TheftController && TheftController->game->inPlay->hasCard(_target))
|
|
{ //if the target is still in game -> spell was destroyed
|
|
_target->changeController(TrueController);
|
|
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AInstantControlSteal ::: TrueController : " << TrueController << " ; TheftController : " << TheftController << " (";
|
|
return InstantAbility::toString(out) << ")";
|
|
}
|
|
|
|
AInstantControlSteal * clone() const
|
|
{
|
|
AInstantControlSteal * a = NEW AInstantControlSteal(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
//Angelic Chorus (10E)
|
|
class AAngelicChorus: public ListMaintainerAbility
|
|
{
|
|
public:
|
|
int init;
|
|
AAngelicChorus(int id, MTGCardInstance * _source) :
|
|
ListMaintainerAbility(id, _source)
|
|
{
|
|
init = 0;
|
|
}
|
|
|
|
void Update(float dt)
|
|
{
|
|
ListMaintainerAbility::Update(dt);
|
|
init = 1;
|
|
}
|
|
|
|
int canBeInList(MTGCardInstance * card)
|
|
{
|
|
if (card->hasType(Subtypes::TYPE_CREATURE) && game->isInPlay(card)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int added(MTGCardInstance * card)
|
|
{
|
|
if (!init) return 0;
|
|
if (source->controller() == game->currentlyActing())
|
|
{
|
|
card->controller()->life += card->toughness;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int removed(MTGCardInstance * card)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
virtual ostream& toString(ostream& out) const
|
|
{
|
|
out << "AAngelicChorus ::: init : " << init << " (";
|
|
return ListMaintainerAbility::toString(out) << ")";
|
|
}
|
|
|
|
AAngelicChorus * clone() const
|
|
{
|
|
AAngelicChorus * a = NEW AAngelicChorus(*this);
|
|
a->isClone = 1;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
// utility functions
|
|
|
|
void PopulateColorIndexVector(list<int>& colors, const string& colorsString, char delimiter = ',');
|
|
void PopulateAbilityIndexVector(list<int>& abilities, const string& abilitiesString, char delimiter = ',');
|
|
void PopulateSubtypesIndexVector(list<int>& subtypes, const string& subtypesString, char delimiter = ' ');
|
|
|
|
#endif
|