diff --git a/projects/mtg/bin/Res/sets/RV/_cards.dat b/projects/mtg/bin/Res/sets/RV/_cards.dat index d76ab9a9f..a78da4bfb 100644 --- a/projects/mtg/bin/Res/sets/RV/_cards.dat +++ b/projects/mtg/bin/Res/sets/RV/_cards.dat @@ -665,6 +665,7 @@ type=Sorcery text={2}, {T}: Untap target attacking creature you control. Prevent all combat damage that would be dealt to and dealt by that creature this turn. id=1108 name=Ebony Horse +auto={2}{T}:untap target(creature[attacking]|myBattlefield) && preventAllCombatDamage to(mytgt) ueot && preventAllCombatDamage from(mytgt) ueot rarity=R mana={3} type=Artifact diff --git a/projects/mtg/bin/Res/test/_tests.txt b/projects/mtg/bin/Res/test/_tests.txt index a135da3bd..8e7cc1e5a 100644 --- a/projects/mtg/bin/Res/test/_tests.txt +++ b/projects/mtg/bin/Res/test/_tests.txt @@ -121,6 +121,7 @@ drift_of_the_dead.txt dromad_purebred.txt dross_harvester.txt duskwalker.txt +ebony_horse.txt elvish_piper.txt elvish_promenade.txt emblem_of_the_warmind.txt diff --git a/projects/mtg/bin/Res/test/ebony_horse.txt b/projects/mtg/bin/Res/test/ebony_horse.txt new file mode 100644 index 000000000..34165c9f0 --- /dev/null +++ b/projects/mtg/bin/Res/test/ebony_horse.txt @@ -0,0 +1,27 @@ +#2, Tap: Untap target attacking creature you control. +# Prevent all combat damage that would be dealt to and dealt by that creature this turn. +[INIT] +COMBATATTACKERS +[PLAYER1] +inplay:grizzly bears,ebony horse,mountain,swamp +[PLAYER2] +inplay:hypnotic specter +[DO] +grizzly bears +next +hypnotic specter +next +yes +mountain +swamp +ebony horse +grizzly bears +endofinterruption +next +[ASSERT] +COMBATEND +[PLAYER1] +inplay:grizzly bears,ebony horse,mountain,swamp +[PLAYER2] +inplay:hypnotic specter +[END] diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index 6704f98ee..79f5f2e65 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -2147,6 +2147,82 @@ public: }; +class APreventAllCombatDamage:public MTGAbility{ + public: + string to, from; + REDamagePrevention * re; + + APreventAllCombatDamage(int id,MTGCardInstance * source,string to,string from):MTGAbility(id,source),to(to),from(from){ + re = NULL; + } + + int addToGame(){ + if (re) { + OutputDebugString("FATAL:re shouldn't be already set in APreventAllCombatDAMAGE\n"); + return 0; + } + TargetChooserFactory tcf; + TargetChooser *toTc = tcf.createTargetChooser(to,source,this); + if (toTc) toTc->targetter = NULL; + TargetChooser *fromTc = tcf.createTargetChooser(from,source,this); + if (fromTc) fromTc->targetter = NULL; + re = NEW REDamagePrevention (this, fromTc, toTc, -1, false, DAMAGE_COMBAT); + game->replacementEffects->add(re); + return 1; + } + + int Destroy(){ + game->replacementEffects->remove(re); + delete re; + return 1; + } + + APreventAllCombatDamage * clone() const{ + APreventAllCombatDamage * a = NEW APreventAllCombatDamage(*this); + a->isClone = 1; + return a; + } + +}; + +//Adds types/abilities/P/T to a card (until end of turn) +class APreventAllCombatDamageUEOT: public InstantAbility{ +public: + APreventAllCombatDamage * ability; + APreventAllCombatDamageUEOT(int id,MTGCardInstance * source,string to, string from):InstantAbility(id,source){ + ability = NEW APreventAllCombatDamage(id,source,to, from); + } + + int resolve(){ + ability->target = this->target; + ability->addToGame(); + return 1; + } + + int destroy(){ + ability->destroy(); + return 1; + } + + const char * getMenuText(){ + return ability->getMenuText(); + } + + + APreventAllCombatDamageUEOT * clone() const{ + APreventAllCombatDamageUEOT * a = NEW APreventAllCombatDamageUEOT(*this); + a->ability = this->ability->clone(); + a->isClone = 1; + return a; + } + + ~APreventAllCombatDamageUEOT(){ + delete ability; + } + +}; + + /* Specific Classes */ @@ -2607,33 +2683,6 @@ class ADisruptingScepter:public TargetAbility{ }; -//1108 Ebony Horse -class AEbonyHorse:public TargetAbility{ - public: - - AEbonyHorse(int _id, MTGCardInstance * _source):TargetAbility(_id,_source, NEW CreatureTargetChooser()){ - int _cost[] = {Constants::MTG_COLOR_ARTIFACT, 2}; - cost = NEW ManaCost(_cost,1); - } - - int resolve(){ - MTGCardInstance * _target = tc->getNextCardTarget(); - if (_target->isAttacker()) _target->toggleAttacker(); - return 1; - } - - virtual ostream& toString(ostream& out) const - { - out << "AEbonyHorse ::: ("; - return TargetAbility::toString(out) << ")"; - } - AEbonyHorse * clone() const{ - AEbonyHorse * a = NEW AEbonyHorse(*this); - a->isClone = 1; - return a; - } -}; - //1345 Farmstead class AFarmstead:public ActivatedAbility{ public: diff --git a/projects/mtg/include/Damage.h b/projects/mtg/include/Damage.h index b02cdd1df..9680f3e42 100644 --- a/projects/mtg/include/Damage.h +++ b/projects/mtg/include/Damage.h @@ -13,6 +13,10 @@ class GameObserver; #define DAMAGEABLE_MTGCARDINSTANCE 0 #define DAMAGEABLE_PLAYER 1 +#define DAMAGE_ALL_TYPES 0 +#define DAMAGE_COMBAT 1 +#define DAMAGE_OTHER 2 + class Damageable:public Targetable { protected: @@ -28,13 +32,14 @@ class Damageable:public Targetable { class Damage: public Interruptible { protected: - void init(MTGCardInstance * source, Damageable * target, int damage); + void init(MTGCardInstance * source, Damageable * target, int damage, int typeOfDamage); public: Damageable * target; + int typeOfDamage; int damage; void Render(); Damage(MTGCardInstance* source, Damageable * target); - Damage(MTGCardInstance* source, Damageable * target, int damage); + Damage(MTGCardInstance* source, Damageable * target, int damage, int typeOfDamage = DAMAGE_OTHER); int resolve(); virtual ostream& toString(ostream& out) const; }; diff --git a/projects/mtg/include/MTGAbility.h b/projects/mtg/include/MTGAbility.h index 79bdb2b0e..1d4e5e821 100644 --- a/projects/mtg/include/MTGAbility.h +++ b/projects/mtg/include/MTGAbility.h @@ -1,249 +1,249 @@ -#ifndef _MTGABILITY_H_ -#define _MTGABILITY_H_ - - - -class MTGCardInstance; -class GameObserver; -class Spell; -class Damageable; -class PlayGuiObject; -class ManaCost; -class MTGGameZone; -class Player; -class AManaProducer; -class WEvent; - -#include "ActionElement.h" -#include -#include -#include -#include "../include/Damage.h" -#include "../include/TargetChooser.h" -using std::string; -using std::map; - - -//stupid variables used to give a hint to the AI: -// Should I cast a spell on an enemy or friendly unit ? -#define BAKA_EFFECT_GOOD 1 -#define BAKA_EFFECT_BAD -1 -#define BAKA_EFFECT_DONTKNOW 0 -#define MODE_PUTINTOPLAY 1 -#define MODE_ABILITY 2 -#define MODE_TARGET 3 - -#define COUNT_POWER 1 - -#define PARSER_LORD 1 -#define PARSER_FOREACH 2 -#define PARSER_ASLONGAS 3 - -class MTGAbility: public ActionElement{ - protected: - char menuText[25]; - - GameObserver * game; - public: - int oneShot; - int forceDestroy; - ManaCost * cost; - Targetable * target; - int aType; - MTGCardInstance * source; - MTGAbility(int id, MTGCardInstance * card); - MTGAbility(int id, MTGCardInstance * _source, Targetable * _target); - virtual int testDestroy(); - virtual ~MTGAbility(); - virtual void Render(){}; - virtual int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){return 0;}; - virtual int reactToClick(MTGCardInstance * card){return 0;}; - virtual int receiveEvent(WEvent * event){return 0;}; - virtual void Update(float dt){}; - virtual int fireAbility(); - virtual int stillInUse(MTGCardInstance * card); - virtual int resolve(){return 0;}; - virtual MTGAbility* clone() const = 0; - virtual ostream& toString(ostream& out) const; - virtual int addToGame(); - virtual int removeFromGame(); - - /*Poor man's casting */ - /* Todo replace that crap with dynamic casting */ - enum { - UNKNOWN = 0, - MANA_PRODUCER = 1, - MTG_ATTACK_RULE = 2, - DAMAGER = 3, - STANDARD_REGENERATE = 4, - PUT_INTO_PLAY = 5, - MOMIR = 6, - MTG_BLOCK_RULE = 7, - }; -}; - - -class TriggeredAbility:public MTGAbility{ - public: - TriggeredAbility(int id, MTGCardInstance * card); - TriggeredAbility(int id, MTGCardInstance * _source, Targetable * _target); - virtual void Update(float dt); - virtual void Render(){}; - virtual int trigger(){return 0;}; - virtual int triggerOnEvent(WEvent * e){return 0;}; - int receiveEvent(WEvent * e); - virtual int resolve() = 0; - virtual TriggeredAbility* clone() const = 0; - virtual ostream& toString(ostream& out) const; -}; - - -class ActivatedAbility:public MTGAbility{ - public: - int playerturnonly; - int needsTapping; - ActivatedAbility(int id, MTGCardInstance * card,ManaCost * _cost = NULL, int _playerturnonly = 0,int tap = 1); - virtual int reactToClick(MTGCardInstance * card); - virtual int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); - virtual int reactToTargetClick(Targetable * object); - virtual int resolve() = 0; - virtual ActivatedAbility* clone() const = 0; - virtual ostream& toString(ostream& out) const; -}; - -class TargetAbility:public ActivatedAbility{ - public: - MTGAbility * ability; - TargetAbility(int id, MTGCardInstance * card, TargetChooser * _tc,ManaCost * _cost = NULL, int _playerturnonly = 0,int tap = 1); - TargetAbility(int id, MTGCardInstance * card,ManaCost * _cost = NULL, int _playerturnonly = 0,int tap = 1); - virtual int reactToClick(MTGCardInstance * card); - virtual int reactToTargetClick(Targetable * object); - virtual TargetAbility* clone() const = 0; - virtual void Render(); - virtual int resolve(); - virtual const char * getMenuText(); - virtual ostream& toString(ostream& out) const; - ~TargetAbility(); -}; - -class InstantAbility:public MTGAbility{ - public: - int init; - virtual void Update(float dt); - virtual int testDestroy(); - InstantAbility(int _id, MTGCardInstance * source); - InstantAbility(int _id, MTGCardInstance * source,Damageable * _target); - virtual int resolve(){return 0;}; - virtual InstantAbility* clone() const = 0; - virtual ostream& toString(ostream& out) const; -}; - -/* State based effects. This class works ONLY for InPlay and needs to be extended for other areas of the game !!! */ -class ListMaintainerAbility:public MTGAbility{ - public: - map cards; - map players; - ListMaintainerAbility(int _id):MTGAbility(_id,NULL){}; - ListMaintainerAbility(int _id, MTGCardInstance *_source):MTGAbility(_id, _source){}; - ListMaintainerAbility(int _id, MTGCardInstance *_source,Damageable * _target):MTGAbility(_id, _source, _target){}; - virtual void Update(float dt); - void updateTargets(); - virtual bool canTarget(MTGGameZone * zone); - virtual int canBeInList(MTGCardInstance * card) = 0; - virtual int added(MTGCardInstance * card) = 0; - virtual int removed(MTGCardInstance * card) = 0; - virtual int canBeInList(Player * p){return 0;}; - virtual int added(Player * p){return 0;}; - virtual int removed(Player * p){return 0;}; - virtual int destroy(); - virtual ListMaintainerAbility* clone() const = 0; - virtual ostream& toString(ostream& out) const; -}; - -class TriggerAtPhase:public TriggeredAbility{ - public: - int phaseId; - int who; - TriggerAtPhase(int id, MTGCardInstance * source, Targetable * target,int _phaseId, int who = 0); - virtual int trigger(); - int resolve(){return 0;}; - virtual TriggerAtPhase* clone() const; -}; - -class TriggerNextPhase:public TriggerAtPhase{ - public: - int destroyActivated; - TriggerNextPhase(int id, MTGCardInstance * source, Targetable * target,int _phaseId, int who = 0); - virtual TriggerNextPhase* clone() const; - virtual int testDestroy(); - -}; - - -class GenericTriggeredAbility:public TriggeredAbility{ - public: - TriggeredAbility * t; - MTGAbility * ability; - MTGAbility * destroyCondition; - GenericTriggeredAbility(int id, MTGCardInstance * _source, TriggeredAbility * _t, MTGAbility * a,MTGAbility * dc = NULL, Targetable * _target = NULL); - virtual int trigger(); - virtual int triggerOnEvent(WEvent * e); - virtual int resolve(); - virtual int testDestroy(); - void Update(float dt); - virtual GenericTriggeredAbility* clone() const; - const char * getMenuText(); - ~GenericTriggeredAbility(); -}; - -/* Ability Factory */ -class AbilityFactory{ - private: - int countCards(TargetChooser * tc, Player * player = NULL, int option = 0); - int parsePowerToughness(string s, int *power, int *toughness); - TriggeredAbility * parseTrigger(string s, int id, Spell * spell, MTGCardInstance *card, Targetable * target); - public: - MTGAbility * parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated = 0, int forceUEOT = 0); - int abilityEfficiency(MTGAbility * a, Player * p, int mode = MODE_ABILITY, TargetChooser * tc = NULL); - int magicText(int id, Spell * spell, MTGCardInstance * card = NULL, int mode = MODE_PUTINTOPLAY, TargetChooser * tc = NULL); - static int computeX(Spell * spell, MTGCardInstance * card); - int destroyAllInPlay(TargetChooser * tc, int bury = 0); - int moveAll(TargetChooser * tc, string destinationZone); - int damageAll(TargetChooser * tc, int damage); - int TapAll(TargetChooser * tc); - int CantBlock(TargetChooser * tc); - int UntapAll(TargetChooser * tc); - void addAbilities(int _id, Spell * spell); -}; - - +#ifndef _MTGABILITY_H_ +#define _MTGABILITY_H_ + + + +class MTGCardInstance; +class GameObserver; +class Spell; +class Damageable; +class PlayGuiObject; +class ManaCost; +class MTGGameZone; +class Player; +class AManaProducer; +class WEvent; + +#include "ActionElement.h" +#include +#include +#include +#include "../include/Damage.h" +#include "../include/TargetChooser.h" +using std::string; +using std::map; + + +//stupid variables used to give a hint to the AI: +// Should I cast a spell on an enemy or friendly unit ? +#define BAKA_EFFECT_GOOD 1 +#define BAKA_EFFECT_BAD -1 +#define BAKA_EFFECT_DONTKNOW 0 +#define MODE_PUTINTOPLAY 1 +#define MODE_ABILITY 2 +#define MODE_TARGET 3 + +#define COUNT_POWER 1 + +#define PARSER_LORD 1 +#define PARSER_FOREACH 2 +#define PARSER_ASLONGAS 3 + +class MTGAbility: public ActionElement{ + protected: + char menuText[25]; + + GameObserver * game; + public: + int oneShot; + int forceDestroy; + ManaCost * cost; + Targetable * target; + int aType; + MTGCardInstance * source; + MTGAbility(int id, MTGCardInstance * card); + MTGAbility(int id, MTGCardInstance * _source, Targetable * _target); + virtual int testDestroy(); + virtual ~MTGAbility(); + virtual void Render(){}; + virtual int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL){return 0;}; + virtual int reactToClick(MTGCardInstance * card){return 0;}; + virtual int receiveEvent(WEvent * event){return 0;}; + virtual void Update(float dt){}; + virtual int fireAbility(); + virtual int stillInUse(MTGCardInstance * card); + virtual int resolve(){return 0;}; + virtual MTGAbility* clone() const = 0; + virtual ostream& toString(ostream& out) const; + virtual int addToGame(); + virtual int removeFromGame(); + + /*Poor man's casting */ + /* Todo replace that crap with dynamic casting */ + enum { + UNKNOWN = 0, + MANA_PRODUCER = 1, + MTG_ATTACK_RULE = 2, + DAMAGER = 3, + STANDARD_REGENERATE = 4, + PUT_INTO_PLAY = 5, + MOMIR = 6, + MTG_BLOCK_RULE = 7, + }; +}; + + +class TriggeredAbility:public MTGAbility{ + public: + TriggeredAbility(int id, MTGCardInstance * card); + TriggeredAbility(int id, MTGCardInstance * _source, Targetable * _target); + virtual void Update(float dt); + virtual void Render(){}; + virtual int trigger(){return 0;}; + virtual int triggerOnEvent(WEvent * e){return 0;}; + int receiveEvent(WEvent * e); + virtual int resolve() = 0; + virtual TriggeredAbility* clone() const = 0; + virtual ostream& toString(ostream& out) const; +}; + + +class ActivatedAbility:public MTGAbility{ + public: + int playerturnonly; + int needsTapping; + ActivatedAbility(int id, MTGCardInstance * card,ManaCost * _cost = NULL, int _playerturnonly = 0,int tap = 1); + virtual int reactToClick(MTGCardInstance * card); + virtual int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); + virtual int reactToTargetClick(Targetable * object); + virtual int resolve() = 0; + virtual ActivatedAbility* clone() const = 0; + virtual ostream& toString(ostream& out) const; +}; + +class TargetAbility:public ActivatedAbility{ + public: + MTGAbility * ability; + TargetAbility(int id, MTGCardInstance * card, TargetChooser * _tc,ManaCost * _cost = NULL, int _playerturnonly = 0,int tap = 1); + TargetAbility(int id, MTGCardInstance * card,ManaCost * _cost = NULL, int _playerturnonly = 0,int tap = 1); + virtual int reactToClick(MTGCardInstance * card); + virtual int reactToTargetClick(Targetable * object); + virtual TargetAbility* clone() const = 0; + virtual void Render(); + virtual int resolve(); + virtual const char * getMenuText(); + virtual ostream& toString(ostream& out) const; + ~TargetAbility(); +}; + +class InstantAbility:public MTGAbility{ + public: + int init; + virtual void Update(float dt); + virtual int testDestroy(); + InstantAbility(int _id, MTGCardInstance * source); + InstantAbility(int _id, MTGCardInstance * source,Damageable * _target); + virtual int resolve(){return 0;}; + virtual InstantAbility* clone() const = 0; + virtual ostream& toString(ostream& out) const; +}; + +/* State based effects. This class works ONLY for InPlay and needs to be extended for other areas of the game !!! */ +class ListMaintainerAbility:public MTGAbility{ + public: + map cards; + map players; + ListMaintainerAbility(int _id):MTGAbility(_id,NULL){}; + ListMaintainerAbility(int _id, MTGCardInstance *_source):MTGAbility(_id, _source){}; + ListMaintainerAbility(int _id, MTGCardInstance *_source,Damageable * _target):MTGAbility(_id, _source, _target){}; + virtual void Update(float dt); + void updateTargets(); + virtual bool canTarget(MTGGameZone * zone); + virtual int canBeInList(MTGCardInstance * card) = 0; + virtual int added(MTGCardInstance * card) = 0; + virtual int removed(MTGCardInstance * card) = 0; + virtual int canBeInList(Player * p){return 0;}; + virtual int added(Player * p){return 0;}; + virtual int removed(Player * p){return 0;}; + virtual int destroy(); + virtual ListMaintainerAbility* clone() const = 0; + virtual ostream& toString(ostream& out) const; +}; + +class TriggerAtPhase:public TriggeredAbility{ + public: + int phaseId; + int who; + TriggerAtPhase(int id, MTGCardInstance * source, Targetable * target,int _phaseId, int who = 0); + virtual int trigger(); + int resolve(){return 0;}; + virtual TriggerAtPhase* clone() const; +}; + +class TriggerNextPhase:public TriggerAtPhase{ + public: + int destroyActivated; + TriggerNextPhase(int id, MTGCardInstance * source, Targetable * target,int _phaseId, int who = 0); + virtual TriggerNextPhase* clone() const; + virtual int testDestroy(); + +}; + + +class GenericTriggeredAbility:public TriggeredAbility{ + public: + TriggeredAbility * t; + MTGAbility * ability; + MTGAbility * destroyCondition; + GenericTriggeredAbility(int id, MTGCardInstance * _source, TriggeredAbility * _t, MTGAbility * a,MTGAbility * dc = NULL, Targetable * _target = NULL); + virtual int trigger(); + virtual int triggerOnEvent(WEvent * e); + virtual int resolve(); + virtual int testDestroy(); + void Update(float dt); + virtual GenericTriggeredAbility* clone() const; + const char * getMenuText(); + ~GenericTriggeredAbility(); +}; + +/* Ability Factory */ +class AbilityFactory{ + private: + int countCards(TargetChooser * tc, Player * player = NULL, int option = 0); + int parsePowerToughness(string s, int *power, int *toughness); + TriggeredAbility * parseTrigger(string s, int id, Spell * spell, MTGCardInstance *card, Targetable * target); + public: + MTGAbility * parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated = 0, int forceUEOT = 0); + int abilityEfficiency(MTGAbility * a, Player * p, int mode = MODE_ABILITY, TargetChooser * tc = NULL); + int magicText(int id, Spell * spell, MTGCardInstance * card = NULL, int mode = MODE_PUTINTOPLAY, TargetChooser * tc = NULL); + static int computeX(Spell * spell, MTGCardInstance * card); + int destroyAllInPlay(TargetChooser * tc, int bury = 0); + int moveAll(TargetChooser * tc, string destinationZone); + int damageAll(TargetChooser * tc, int damage); + int TapAll(TargetChooser * tc); + int CantBlock(TargetChooser * tc); + int UntapAll(TargetChooser * tc); + void addAbilities(int _id, Spell * spell); +}; + + class ActivatedAbilityTP:public ActivatedAbility{ public: int who; ActivatedAbilityTP(int id, MTGCardInstance * card, Targetable * _target = NULL, ManaCost * cost=NULL, int doTap = 0, int who = TargetChooser::UNSET); Targetable * getTarget(); -}; - -class AManaProducer: public ActivatedAbilityTP{ - protected: - - - string menutext; - Player * controller; - - public: - ManaCost * output; - int tap; - AManaProducer(int id, MTGCardInstance * card, Targetable * t, ManaCost * _output, ManaCost * _cost = NULL, int doTap = 1, int who = TargetChooser::UNSET ); - int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL); - int resolve(); - int reactToClick(MTGCardInstance * _card); - const char * getMenuText(); - ~AManaProducer(); - virtual AManaProducer * clone() const; -}; - -#include "MTGCardInstance.h" - -#endif - +}; + +class AManaProducer: public ActivatedAbilityTP{ + protected: + + + string menutext; + Player * controller; + + public: + ManaCost * output; + int tap; + AManaProducer(int id, MTGCardInstance * card, Targetable * t, ManaCost * _output, ManaCost * _cost = NULL, int doTap = 1, int who = TargetChooser::UNSET ); + int isReactingToClick(MTGCardInstance * _card, ManaCost * mana = NULL); + int resolve(); + int reactToClick(MTGCardInstance * _card); + const char * getMenuText(); + ~AManaProducer(); + virtual AManaProducer * clone() const; +}; + +#include "MTGCardInstance.h" + +#endif + diff --git a/projects/mtg/include/PlayGuiObject.h b/projects/mtg/include/PlayGuiObject.h index c031cb689..d79034cff 100644 --- a/projects/mtg/include/PlayGuiObject.h +++ b/projects/mtg/include/PlayGuiObject.h @@ -24,7 +24,7 @@ class PlayGuiObject: public JGuiObject, public JGuiListener, public Pos{ float defaultHeight; bool mHasFocus; int type; - virtual void Entering(){mHasFocus = true; zoom = 1.4;}; + virtual void Entering(){mHasFocus = true; zoom = 1.4f;}; virtual bool Leaving(u32 key){mHasFocus = false; zoom = 1.0; return true;}; virtual bool CheckUserInput(u32 key) {return false;}; virtual bool ButtonPressed(){return true;}; diff --git a/projects/mtg/include/ReplacementEffects.h b/projects/mtg/include/ReplacementEffects.h index 09af0ff9d..dfffd4429 100644 --- a/projects/mtg/include/ReplacementEffects.h +++ b/projects/mtg/include/ReplacementEffects.h @@ -3,6 +3,7 @@ #include using namespace std; +#include "../include/Damage.h" #include "WEvent.h" class TargetChooser; @@ -21,8 +22,9 @@ protected: TargetChooser * tcTarget; int damage; bool oneShot; + int typeOfDamage; public: - REDamagePrevention(MTGAbility * _source, TargetChooser *_tcSource = NULL,TargetChooser *_tcTarget = NULL, int _damage = -1, bool _oneShot = true); + REDamagePrevention(MTGAbility * _source, TargetChooser *_tcSource = NULL,TargetChooser *_tcTarget = NULL, int _damage = -1, bool _oneShot = true, int typeOfDamage = DAMAGE_ALL_TYPES); WEvent * replace (WEvent *e); ~REDamagePrevention(); }; diff --git a/projects/mtg/include/TargetChooser.h b/projects/mtg/include/TargetChooser.h index e8dd889df..388969f3d 100644 --- a/projects/mtg/include/TargetChooser.h +++ b/projects/mtg/include/TargetChooser.h @@ -58,7 +58,7 @@ class TargetChooser: public TargetsList { class TargetChooserFactory{ public: - TargetChooser * createTargetChooser(string s, MTGCardInstance * card); + TargetChooser * createTargetChooser(string s, MTGCardInstance * card, MTGAbility * ability = NULL); TargetChooser * createTargetChooser(MTGCardInstance * card); }; diff --git a/projects/mtg/src/Damage.cpp b/projects/mtg/src/Damage.cpp index 31f08e68c..8f13b5cba 100644 --- a/projects/mtg/src/Damage.cpp +++ b/projects/mtg/src/Damage.cpp @@ -7,14 +7,15 @@ #include "../include/WResourceManager.h" Damage::Damage(MTGCardInstance * source, Damageable * target) { - init(source, target, source->getPower()); + init(source, target, source->getPower(), DAMAGE_OTHER); } -Damage::Damage(MTGCardInstance * source, Damageable * target, int damage) { - init(source, target, damage); +Damage::Damage(MTGCardInstance * source, Damageable * target, int damage,int _typeOfDamage) { + init(source, target, damage, _typeOfDamage); } -void Damage::init(MTGCardInstance * _source, Damageable * _target, int _damage){ +void Damage::init(MTGCardInstance * _source, Damageable * _target, int _damage, int _typeOfDamage){ + typeOfDamage = _typeOfDamage; target = _target; source = _source; @@ -70,7 +71,6 @@ int Damage::resolve(){ //Send (Damage/Replaced effect) event to listeners g->receiveEvent(e); - //SAFE_DELETE(e); return a; } diff --git a/projects/mtg/src/DamagerDamaged.cpp b/projects/mtg/src/DamagerDamaged.cpp index 3bbb58a3c..6f4ce5bdf 100644 --- a/projects/mtg/src/DamagerDamaged.cpp +++ b/projects/mtg/src/DamagerDamaged.cpp @@ -28,7 +28,7 @@ void DamagerDamaged::addDamage(int damage, DamagerDamaged* source){ if (0 >= i->damage) damages.erase(i); return; } - if (0 < damage) damages.push_back(Damage(source->card, card, damage)); + if (0 < damage) damages.push_back(Damage(source->card, card, damage,DAMAGE_COMBAT)); return; } diff --git a/projects/mtg/src/GuiCombat.cpp b/projects/mtg/src/GuiCombat.cpp index 8454ddfe1..6a466d73b 100644 --- a/projects/mtg/src/GuiCombat.cpp +++ b/projects/mtg/src/GuiCombat.cpp @@ -314,7 +314,7 @@ int GuiCombat::resolve() // Returns the number of damage objects dealt this turn stack->Add(NEW Damage(*d)); dmg -= (*q)->sumDamages(); } - if (dmg > 0) stack->Add(NEW Damage((*it)->card, go->opponent(), dmg)); + if (dmg > 0) stack->Add(NEW Damage((*it)->card, go->opponent(), dmg, DAMAGE_COMBAT)); for (vector::iterator d = (*it)->damages.begin(); d != (*it)->damages.end(); ++d) stack->Add(NEW Damage(*d)); } diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 0ea75894a..587c3414d 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -1,2213 +1,2238 @@ -#include "../include/config.h" -#include "../include/MTGAbility.h" -#include "../include/ManaCost.h" -#include "../include/MTGGameZones.h" -#include "../include/AllAbilities.h" -#include "../include/Damage.h" -#include "../include/TargetChooser.h" -#include "../include/CardGui.h" -#include "../include/MTGDeck.h" -#include "../include/Blocker.h" -#include "../include/Translate.h" - - -int AbilityFactory::countCards(TargetChooser * tc, Player * player, int option){ - int result = 0; - GameObserver * game = GameObserver::GetInstance(); - for (int i = 0; i < 2 ; i++){ - if (player && player!= game->players[i]) continue; - MTGGameZone * zones[] = {game->players[i]->game->inPlay,game->players[i]->game->graveyard,game->players[i]->game->hand}; - for (int k = 0; k < 3; k++){ - for (int j = zones[k]->nb_cards-1; j >=0 ; j--){ - MTGCardInstance * current = zones[k]->cards[j]; - if (tc->canTarget(current)){ - switch (option){ - case COUNT_POWER: - result+= current->power; - break; - default: - result++; - break; - } - } - } - } - } - return result; -} - - - -int AbilityFactory::CantBlock(TargetChooser * tc){ - GameObserver * g = GameObserver::GetInstance(); - MTGCardInstance * source = tc->source; - for (int j = g->opponent()->game->inPlay->nb_cards-1; j >=0 ; j--){ - MTGCardInstance * current = g->opponent()->game->inPlay->cards[j]; - if (tc->canTarget(current)){ - current->canBlock(source); - return 0; - } - } - return 1; -} - - -int AbilityFactory::parsePowerToughness(string s, int *power, int *toughness){ - size_t found = s.find("/"); - 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 = -1; - - *power = atoi(s.substr(start+1,s.size()-found).c_str()); - *toughness = atoi(s.substr(found+1,end-found-1).c_str()); - - return 1; - } - return 0; -} - -TriggeredAbility * AbilityFactory::parseTrigger(string magicText, int id, Spell * spell, MTGCardInstance *card, Targetable * target){ - size_t found = magicText.find("@"); - if (found == string::npos) return NULL; - - found = magicText.find(":"); - if (found == string::npos) return NULL; - string s = magicText.substr(0,found); - - //Card Changed Zone - found = s.find("movedto("); - if (found != string::npos){ - size_t end = s.find (")"); - string starget = s.substr(found+8,end - found - 8); - TargetChooserFactory tcf; - TargetChooser *toTc = tcf.createTargetChooser(starget,card); - toTc->targetter = NULL; - - TargetChooser *fromTc = NULL; - found = s.find("from("); - if (found != string::npos){ - end = s.find (")", found); - starget = s.substr(found+5,end - found - 5); - if (starget.find("|") == string::npos) starget.insert(0,"*|"); - fromTc = tcf.createTargetChooser(starget,card); - fromTc->targetter = NULL; - } - return NEW TrCardAddedToZone(id,card,toTc,(TargetZoneChooser *)fromTc); - } - - //Card Tapped - found = s.find("tapped("); - if (found != string::npos){ - size_t end = s.find (")"); - string starget = s.substr(found+7,end - found - 7); - TargetChooserFactory tcf; - TargetChooser *tc = tcf.createTargetChooser(starget,card); - tc->targetter = NULL; - - return NEW TrCardTapped(id,card,tc); - } - - //Card Damaging - found = s.find("damaged("); - if (found != string::npos){ - size_t end = s.find (")"); - string starget = s.substr(found+8,end - found - 8); - TargetChooserFactory tcf; - TargetChooser *tc = tcf.createTargetChooser(starget,card); - tc->targetter = NULL; - found = s.find("from("); - - TargetChooser *fromTc = NULL; - if (found != string::npos){ - end = s.find (")", found); - starget = s.substr(found+5,end - found - 5); - fromTc = tcf.createTargetChooser(starget,card); - fromTc->targetter = NULL; - } - return NEW TrDamaged(id,card,tc,fromTc); - } - - int who = 0; - if (s.find("my") != string::npos) who = 1; - if (s.find("opponent") != string::npos) who = -1; - if (s.find("targetcontroller") != string::npos) who = -2; - - //Next Time... - found = s.find("next"); - if (found != string::npos){ - for (int i = 0; i < Constants::NB_MTG_PHASES; i++){ - found = s.find(Constants::MTGPhaseCodeNames[i]); - if (found != string::npos){ - return NEW TriggerNextPhase(id, card,target,i,who); - } - } - } - - //Each Time... - found = magicText.find("each"); - if (found != string::npos){ - for (int i = 0; i < Constants::NB_MTG_PHASES; i++){ - found = magicText.find(Constants::MTGPhaseCodeNames[i]); - if (found != string::npos){ - return NEW TriggerAtPhase(id, card,target,i,who); - } - } - } - - return NULL; -} - - - - - -//Parses a string and returns the corresponding MTGAbility object -// Returns NULL if parsing failed -//Beware, Spell CAN be null when the function is called by the AI trying to analyze the effects of a given card -MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated, int forceUEOT){ - size_t found; - - string whitespaces (" \t\f\v\n\r"); - - found=s.find_last_not_of(whitespaces); - if (found!=string::npos) - s.erase(found+1); - else return NULL; - - found=s.find_first_not_of(whitespaces); - if (found!=string::npos) - s.erase(0,found); - else return NULL; - - //TODO This block redundant with calling function - if (!card && spell) card = spell->source; - if (!card) return NULL; - MTGCardInstance * target = card->target; - if (!target) target = card; - - TriggeredAbility * trigger = NULL; - trigger = parseTrigger(s,id,spell,card,target); - //Dirty way to remove the trigger text (could get in the way) - if (trigger){ - found = s.find(":"); - string s1 = s.substr(found+1); - MTGAbility * a = parseMagicLine(s1, id, spell, card,activated); - if (!a){ - delete trigger; - return NULL; - } - return NEW GenericTriggeredAbility(id,card,trigger,a,NULL,target); - } - - int doTap = 0; //Tap in the cost ? - if (s.find("{t}") != string::npos) doTap = 1; - - size_t delimiter = s.find("}:"); - size_t firstNonSpace = s.find_first_not_of(" "); - if (delimiter!= string::npos && firstNonSpace !=string::npos && s[firstNonSpace] == '{'){ - ManaCost * cost = ManaCost::parseManaCost(s.substr(0,delimiter+1),NULL,card); - if (doTap || (cost && !cost->isNull())){ - string s1 = s.substr(delimiter+2); - - MTGAbility * a = parseMagicLine(s1, id, spell, card, 1); - if (!a){ - OutputDebugString("ABILITYFACTORY Error parsing:"); - OutputDebugString(s.c_str()); - OutputDebugString("\n"); - return NULL; - } - - //A stupid Special case for ManaProducers because they don't use the stack :( - AManaProducer * amp = dynamic_cast(a); - if (amp){ - amp->cost = cost; - amp->oneShot = 0; - amp->tap = doTap; - return amp; - } - - int limit = 0; - unsigned int limit_str = s.find("limit:"); - if (limit_str != string::npos){ - limit = atoi(s.substr(limit_str+6).c_str()); - } - - TargetChooser * tc = NULL; - //Target Abilities - found = s.find("target("); - if (found != string::npos){ - int end = s.find(")", found); - string starget = s.substr(found + 7,end - found - 7); - TargetChooserFactory tcf; - tc = tcf.createTargetChooser(starget, card); - } - - if (tc) return NEW GenericTargetAbility(id, card, tc, a,cost, doTap,limit); - return NEW GenericActivatedAbility(id, card, a,cost,doTap,limit); - } - SAFE_DELETE(cost); - } - - //kicker cost - found = s.find("kicker "); - if (found == 0){ - if (spell && spell->kickerWasPaid()){ - string s1 = s.substr(found+7); - return parseMagicLine(s1,id,spell, card); - } - return NULL; - } - //When...comes into play, you may... - found = s.find("may "); - if (found == 0){ - string s1 = s.substr(found+4); - MTGAbility * a1 = parseMagicLine(s1,id,spell, card); - if (!a1) return NULL; - TargetChooser * tc = NULL; - //Target Abilities - found = s.find("target("); - if (found != string::npos){ - int end = s.find(")", found); - string starget = s.substr(found + 7,end - found - 7); - TargetChooserFactory tcf; - tc = tcf.createTargetChooser(starget, card); - } - if (tc) a1 = NEW GenericTargetAbility(id, card, tc, a1); - else a1 = NEW GenericActivatedAbility(id, card, a1,NULL); - return NEW MayAbility(id,a1,card); - } - - - //Multiple abilities for ONE cost - found = s.find("&&"); - if (found != string::npos){ - string s1 = s.substr(0,found); - string s2 = s.substr(found+2); - MultiAbility * multi = NEW MultiAbility(id, card,target,NULL,NULL); - MTGAbility * a1 = parseMagicLine(s1,id,spell, card,activated); - MTGAbility * a2 = parseMagicLine(s2,id,spell, card,activated); - multi->Add(a1); - multi->Add(a2); - multi->oneShot=1; - return multi; - } - - //Lord, foreach, aslongas - string lords[] = {"lord(","foreach(", "aslongas(", "all("}; - found = string::npos; - int i = -1; - for (int j = 0; j < 4; ++j){ - size_t found2 = s.find(lords[j]); - if (found2!=string::npos && ((found == string::npos) || found2 < found)){ - found = found2; - i = j; - } - } - if (found != string::npos){ - size_t header = lords[i].size(); - size_t end = s.find(")", found+header); - string s1; - if (found == 0 || end != s.size()-1){ - s1 = s.substr(end+1); - }else{ - s1 = s.substr(0, found); - } - if (end != string::npos){ - int lordIncludeSelf = 1; - size_t other = s1.find("other"); - if ( other != string::npos){ - lordIncludeSelf = 0; - s1.replace(other, 5,""); - } - string lordTargetsString = s.substr(found+header,end-found-header); - TargetChooserFactory tcf; - TargetChooser * lordTargets = tcf.createTargetChooser(lordTargetsString, card); - - if (!lordTargets){ - OutputDebugString("MTGABILITY: Parsing Error:"); - OutputDebugString(s.c_str()); - OutputDebugString("\n"); - return NULL; - } - - MTGAbility * a = parseMagicLine(s1,id,spell, card,0,activated); //activated lords usually force an end of turn ability - if (!a){ - SAFE_DELETE(lordTargets); - return NULL; - } - MTGAbility * result = NULL; - int oneShot = 0; - if (activated) oneShot = 1; - if (card->hasType("sorcery") || card->hasType("instant")) oneShot = 1; - if (i == 3) oneShot = 1; - if (a->oneShot) oneShot = 1; - Damageable * _target = NULL; - if (spell) _target = spell->getNextDamageableTarget(); - if (!_target) _target = target; - switch(i){ - case 0: result = NEW ALord(id, card, lordTargets, lordIncludeSelf, a); break; - case 1: result = NEW AForeach(id, card, _target,lordTargets, lordIncludeSelf, a); break; - case 2: result = NEW AAsLongAs(id, card, _target,lordTargets, lordIncludeSelf, a); break; - case 3: result = NEW ALord(id, card, lordTargets, lordIncludeSelf, a); break; - default: result = NULL; - } - if (result) result->oneShot = oneShot; - return result; - } - return NULL; - } - - - //Fizzle (counterspell...) - found = s.find("fizzle"); - if (found != string::npos){ - Spell * starget = NULL; - if (spell) starget = spell->getNextSpellTarget(); - MTGAbility * a = NEW AAFizzler(id,card,starget); - a->oneShot = 1; - return a; - } - - - //Regeneration - found = s.find("regenerate"); - if (found != string::npos){ - MTGAbility * a = NEW AStandardRegenerate(id,card,target); - a->oneShot = 1; - return a; - } - - - //Token creator. Name, type, p/t, abilities - found = s.find("token("); - if (found != string::npos){ - int end = s.find(",", found); - string sname = s.substr(found + 6,end - found - 6); - int previous = end+1; - end = s.find(",",previous); - string stypes = s.substr(previous,end - previous); - previous = end+1; - end = s.find(",",previous); - string spt = s.substr(previous,end - previous); - int power, toughness; - parsePowerToughness(spt,&power, &toughness); - string sabilities = s.substr(end+1); - WParsedInt * multiplier = NULL; - found = s.find("*"); - if (found != string::npos)multiplier = NEW WParsedInt(s.substr(found+1),spell,card); - ATokenCreator * tok = NEW ATokenCreator(id,card,NULL,sname,stypes,power,toughness,sabilities,0, multiplier); - tok->oneShot = 1; - return tok; - } - - - //MoveTo Move a card from a zone to another - found = s.find("moveto("); - if (found != string::npos){ - int end = s.find(")",found+1); - string szone = s.substr(found + 7,end - found - 7); - - //hack for http://code.google.com/p/wagic/issues/detail?id=120 - //We assume that auras don't move their own target... - if (card->hasType("aura")) target = card; - - MTGAbility * a = NEW AAMover(id,card,target,szone); - a->oneShot = 1; - return a; - - } - - //Copy a target - found = s.find("copy "); - if (found != string::npos){ - MTGAbility * a = NEW AACopier(id,card,target); - a->oneShot = 1; - return a; - } - - - //Bury, destroy - string destroys[] = {"bury","destroy"}; - int destroyTypes[]= {1, 0}; - for (int i = 0; i < 2; ++i){ - found = s.find(destroys[i]); - if (found != string::npos){ - int bury = destroyTypes[i]; - MTGAbility * a = NEW AADestroyer(id,card,target,bury); - a->oneShot = 1; - return a; - } - } - - - int who = TargetChooser::UNSET; - if (s.find(" controller") != string::npos) who=TargetChooser::CONTROLLER; - if (s.find(" opponent") != string::npos) who=TargetChooser::OPPONENT; - if (s.find(" targetcontroller") != string::npos) who=TargetChooser::TARGET_CONTROLLER; - - //Damage - found = s.find("damage"); - if (found != string::npos){ - unsigned int start = s.find(":",found); - if (start == string::npos) start = s.find(" ",found); - unsigned int end = s.find(" ",start); - int damage; - if (end != string::npos){ - damage = atoi(s.substr(start+1,end-start-1).c_str()); - }else{ - damage = atoi(s.substr(start+1).c_str()); - } - - Damageable * d = NULL; - if (spell) d = spell->getNextDamageableTarget(); - MTGAbility * a = NEW AADamager(id,card,d, damage, NULL, 0, who); - a->oneShot = 1; - return a; - } - - - //gain/lose life - found = s.find("life:"); - if (found != string::npos){ - unsigned int start = found+4; - unsigned int end = s.find(" ",start); - int life; - if (end != string::npos){ - life = atoi(s.substr(start+1,end-start-1).c_str()); - }else{ - life = atoi(s.substr(start+1).c_str()); - } - - Damageable * d = NULL; - if (spell) d = spell->getNextPlayerTarget(); - MTGAbility * a = NEW AALifer(id,card,d,life,NULL,0,who); - a->oneShot = 1; - return a; - } - - //Draw - found = s.find("draw:"); - if (found != string::npos){ - unsigned int start = s.find(":",found); - unsigned int end = s.find(" ",start); - int nbcards; - if (end != string::npos){ - nbcards = atoi(s.substr(start+1,end-start-1).c_str()); - }else{ - nbcards = atoi(s.substr(start+1).c_str()); - } - - Targetable * t = NULL; - if (spell) t = spell->getNextPlayerTarget(); - MTGAbility * a = NEW AADrawer(id,card,t,NULL,nbcards,0,who); - a->oneShot = 1; - return a; - } - - //Deplete - found = s.find("deplete:"); - if (found != string::npos){ - unsigned int start = s.find(":",found); - unsigned int end = s.find(" ",start); - int nbcards; - if (end != string::npos){ - nbcards = atoi(s.substr(start+1,end-start-1).c_str()); - }else{ - nbcards = atoi(s.substr(start+1).c_str()); - } - - Targetable * t = NULL; - if (spell) t = spell->getNextPlayerTarget(); - MTGAbility * a = NEW AADepleter(id,card,t,nbcards,NULL,0,who); - a->oneShot = 1; - return a; - } - - //Shuffle - found = s.find("shuffle"); - if (found != string::npos){ - Targetable * t = NULL; - if (spell) t = spell->getNextPlayerTarget(); - MTGAbility * a = NEW AAShuffle(id,card,t,NULL,0,who); - a->oneShot = 1; - return a; - } - - /* - //CannotBeBlockedBy - found = s.find("cantbeblockedby("); - if (found != string::npos){ - int end = s.find(")",found); - string starget = s.substr(16, end - 16); - TargetChooserFactory tcf; - tc = tcf.createTargetChooser(starget,card); - return NULL; //NEW ACantBlock(tc); //hu ? CantBlock(tc); - } - -*/ - //Discard - found = s.find("discard:"); - if (found != string::npos){ - unsigned int start = s.find(":",found); - unsigned int end = s.find(" ",start); - int nbcards; - if (end != string::npos){ - nbcards = atoi(s.substr(start+1,end-start-1).c_str()); - }else{ - nbcards = atoi(s.substr(start+1).c_str()); - } - - Targetable * t = NULL; - if (spell) t = spell->getNextPlayerTarget(); - MTGAbility * a = NEW AARandomDiscarder (id, card, t,nbcards,NULL,0,who); - a->oneShot = 1; - return a; - } - - - - //rampage - found = s.find("rampage("); - if (found != string::npos){ - int end = s.find(",", found); - string spt = s.substr(8,end - 1); - int power, toughness; - if (parsePowerToughness(spt,&power, &toughness)){ - int MaxOpponent = atoi(s.substr(end+1,end+2).c_str()); - return NEW ARampageAbility(id,card,power,toughness,MaxOpponent); - } - return NULL; - } - - - //counter - found = s.find("counter("); - if (found != string::npos){ - found+=8; - int nb = 1; - size_t end = s.find(")", found); - size_t separator = s.find(",", found); - if (separator != string::npos){ - string nbstr = s.substr(separator+1,end-separator-1); - nb = atoi(nbstr.c_str()); - end = separator; - } - string spt = s.substr(found,end-found); - int power, toughness; - if ( parsePowerToughness(spt,&power, &toughness)){ - MTGAbility * a = NEW AACounter(id,card,target,power,toughness,nb); - a->oneShot = 1; - return a; - } - return NULL; - } - - - found = s.find("ueot"); - if (found!= string::npos) forceUEOT = 1; - - - //Becomes... (animate artifact...: becomes(Creature, manacost/manacost) - found = s.find("becomes("); - if (found != string::npos){ - size_t real_end = s.find(")", found); - size_t end = s.find(",", found); - if (end == string::npos) end = real_end; - string stypes = s.substr(found + 8,end - found - 8); - WParsedPT * pt = NULL; - string sabilities; - if (end != real_end){ - int previous = end+1; - end = s.find(",",previous); - if (end == string::npos) end = real_end; - string temp = s.substr(previous, end - previous); - pt = NEW WParsedPT(temp,spell,card); - if (!pt->ok){ - SAFE_DELETE(pt); - sabilities = temp; - } - } - if (pt && end != real_end){ - sabilities = s.substr(end+1, real_end - end); - } - MTGAbility * ab; - if (forceUEOT){ - ab = NEW ABecomesUEOT(id,card,target,stypes,pt,sabilities); - }else{ - ab = NEW ABecomes(id,card,target,stypes,pt,sabilities); - } - return ab; - } - - //Change Power/Toughness - WParsedPT * wppt = NEW WParsedPT(s,spell,card); - if (wppt->ok){ - if (!activated){ - if(card->hasType("instant") || card->hasType("sorcery") || forceUEOT){ - return NEW AInstantPowerToughnessModifierUntilEOT(id, card, target,wppt); - } - return NEW APowerToughnessModifier(id, card, target,wppt); - } - return NEW APowerToughnessModifierUntilEndOfTurn(id,card,target,wppt); - }else{ - delete wppt; - } - - - - //Mana Producer - found = s.find("add"); - if (found != string::npos){ - ManaCost * output = ManaCost::parseManaCost(s.substr(found)); - Targetable * t = NULL; - if (spell) t = spell->getNextPlayerTarget(); - MTGAbility * a = NEW AManaProducer(id, card, t, output, NULL, 1, who); - a->oneShot = 1; - return a; - } - - - //Gain/loose Ability - for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++){ - found = s.find(Constants::MTGBasicAbilities[j]); - if (found == 0 || found == 1){ - int modifier = 1; - if (found > 0 && s[found-1] == '-') modifier = 0; - if (!activated){ - if(card->hasType("instant") || card->hasType("sorcery") || forceUEOT){ - return NEW AInstantBasicAbilityModifierUntilEOT(id, card,target, j,modifier); - } - return NEW ABasicAbilityModifier(id, card,target, j,modifier); - } - return NEW ABasicAbilityAuraModifierUntilEOT(id, card,target, NULL,j,modifier); - } - } - - //Untapper (Ley Druid...) - found = s.find("untap"); - if (found != string::npos){ - MTGAbility * a = NEW AAUntapper(id,card,target); - a->oneShot = 1; - return a; - } - - //Tapper (icy manipulator) - found = s.find("tap"); - if (found != string::npos){ - MTGAbility * a = NEW AATapper(id,card,target); - a->oneShot = 1; - return a; - } - - return NULL; - -} - -//Tells the AI if the ability should target itself or an ennemy -int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, TargetChooser * tc){ - if (!a) return BAKA_EFFECT_DONTKNOW; - - if (GenericTargetAbility * abi = dynamic_cast(a)) { - if (mode == MODE_PUTINTOPLAY) return BAKA_EFFECT_GOOD; - return abilityEfficiency(abi->ability,p, mode, abi->tc); - } - if (GenericActivatedAbility * abi = dynamic_cast(a)) { - if (mode == MODE_PUTINTOPLAY) return BAKA_EFFECT_GOOD; - return abilityEfficiency(abi->ability,p, mode,tc); - } - if (MultiAbility * abi = dynamic_cast(a)) return abilityEfficiency(abi->abilities[0],p, mode,tc ); - if (MayAbility * abi = dynamic_cast(a)) return abilityEfficiency(abi->ability,p, mode,tc); - if (ALord * abi = dynamic_cast(a)) { - int myCards = countCards(abi->tc, p); - int theirCards = countCards(abi->tc, p->opponent()); - int efficiency = abilityEfficiency(abi->ability,p, mode,tc); - if (myCards > theirCards) return efficiency; - return -efficiency; - } - if (AAsLongAs * abi = dynamic_cast(a)) return abilityEfficiency(abi->ability,p, mode,tc); - if (AForeach * abi = dynamic_cast(a)) return abilityEfficiency(abi->ability,p, mode,tc); - if (dynamic_cast(a)) return BAKA_EFFECT_BAD; - if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; - if (dynamic_cast(a)) return BAKA_EFFECT_BAD; - if (AACounter * ac = dynamic_cast(a)) { - bool negative_effect = ac->power < 0 || ac->toughness < 0; - if ((ac->nb > 0 && negative_effect) || (ac->nb < 0 && !negative_effect)) return BAKA_EFFECT_BAD; - return BAKA_EFFECT_GOOD ; - } - if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; - - if (AAMover * aam = dynamic_cast(a)) { - MTGGameZone * z = aam->destinationZone(); - if (tc && tc->targetsZone(p->game->library)){ - if (z == p->game->hand || z == p->game->inPlay) return BAKA_EFFECT_GOOD; - } - return BAKA_EFFECT_BAD; //TODO - } - - if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; - if (dynamic_cast(a)) return BAKA_EFFECT_BAD; - if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; - if (dynamic_cast(a)) return BAKA_EFFECT_BAD; - if (AALifer * abi = dynamic_cast(a)) return abi->life > 0 ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD; - if (dynamic_cast(a)) return BAKA_EFFECT_BAD; - if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; - if (dynamic_cast(a)) return BAKA_EFFECT_BAD; - if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; - if (AInstantPowerToughnessModifierUntilEOT * abi = dynamic_cast(a)) return (abi->wppt->power.getValue()>=0 && abi->wppt->toughness.getValue()>=0) ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD; - if (APowerToughnessModifier * abi = dynamic_cast(a)) return (abi->wppt->power.getValue()>=0 && abi->wppt->toughness.getValue()>=0) ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD; - if (APowerToughnessModifierUntilEndOfTurn * abi = dynamic_cast(a)) return abilityEfficiency(abi->ability, p, mode,tc); - - map badAbilities; - badAbilities[Constants::CANTATTACK] = true; - badAbilities[Constants::CANTBLOCK] = true; - badAbilities[Constants::CLOUD] = true; - badAbilities[Constants::DEFENDER] = true; - badAbilities[Constants::DOESNOTUNTAP] = true; - badAbilities[Constants::MUSTATTACK] = true; - - if (AInstantBasicAbilityModifierUntilEOT * abi = dynamic_cast(a)) { - int result = badAbilities[abi->ability] ? BAKA_EFFECT_BAD : BAKA_EFFECT_GOOD; - return (abi->value > 0) ? result : -result; - } - if (ABasicAbilityModifier * abi = dynamic_cast(a)){ - int result = (badAbilities[abi->ability]) ? BAKA_EFFECT_BAD : BAKA_EFFECT_GOOD; - return (abi->modifier > 0) ? result : -result; - } - if (ABasicAbilityAuraModifierUntilEOT * abi = dynamic_cast(a)) - return abilityEfficiency(abi->ability, p, mode); - if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; - return BAKA_EFFECT_DONTKNOW; -} - -//Returns the "X" cost that was paid for a spell -int AbilityFactory::computeX(Spell * spell, MTGCardInstance * card){ - if (spell) return spell->computeX(card); - return 0; -} - -//Some basic functionalities that can be added automatically in the text file -/* - * Several objects are computed from the text string, and have a direct influence on what action we should take - * (direct impact on the game such as draw a card immediately, or create a New GameObserver and add it to the Abilities,etc..) - * These objects are: - * - trigger (if there is an "@" in the string, this is a triggered ability) - * - target (if there ie a "target(" in the string, then this is a TargetAbility) - * - doTap (a dirty way to know if tapping is included in the cost... - */ -int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int mode, TargetChooser * tc){ - int dryMode = 0; - if (!spell) dryMode = 1; - - if (!card && spell) card = spell->source; - if (!card) return 0; - MTGCardInstance * target = card->target; - if (!target) target = card; - string magicText = card->magicText; - if (card->alias && magicText.size() == 0){ - //An awful way to get access to the aliasedcard - MTGCard * c = GameObserver::GetInstance()->players[0]->game->collection->getCardById(card->alias); - if (!c) return 0; - magicText = c->magicText; - } - string line; - int size = magicText.size(); - if (size == 0) return 0; - unsigned int found; - int result = id; - - - while (magicText.size()){ - found = magicText.find("\n"); - if (found != string::npos){ - line = magicText.substr(0,found); - magicText = magicText.substr(found+1); - }else{ - line = magicText; - magicText = ""; - } - - MTGAbility * a = parseMagicLine(line, result, spell, card); - if (dryMode){ - result = abilityEfficiency(a, card->controller(),mode,tc); - SAFE_DELETE(a); - return result; - } - - if (a){ - if (a->oneShot){ - a->resolve(); - delete(a); - }else{ - a->addToGame(); - } - result++; - }else{ - OutputDebugString("ABILITYFACTORY ERROR: Parser returned NULL\n"); - } - } - return result; - -} - -void AbilityFactory::addAbilities(int _id, Spell * spell){ - MTGCardInstance * card = spell->source; - - - if (spell->getNbTargets()==1){ - card->target = spell->getNextCardTarget(); - if (card->target && !spell->tc->canTarget(card->target)){ - MTGPlayerCards * zones = card->controller()->game; - zones->putInZone(card,spell->from,card->owner->game->graveyard); - return; //fizzle - } - } - _id = magicText(_id, spell); - - GameObserver * game = GameObserver::GetInstance(); - MTGPlayerCards * zones = card->controller()->game; - - - int id = card->getId(); - if (card->alias) id = card->alias; - switch (id){ - case 1092: //Aladdin's lamp - { - AAladdinsLamp * ability = NEW AAladdinsLamp(_id, card); - game->addObserver(ability); - break; - } - case 1095: //Armageddon clock - { - AArmageddonClock * ability = NEW AArmageddonClock(_id,card); - game->addObserver(ability); - break; - } - case 1097: //Black Vise - { - game->addObserver( NEW ALifeZoneLink(_id ,card, Constants::MTG_PHASE_UPKEEP, 4)); - break; - } - case 1191: //Blue Elemental Blast - { - if (card->target){ - card->target->controller()->game->putInGraveyard(card->target); - }else{ - Spell * starget = spell->getNextSpellTarget(); - game->mLayers->stackLayer()->Fizzle(starget); - } - break; - } - case 1237: //Channel - { - game->addObserver(NEW AChannel(_id, card)); - break; - } - case 1282: //Chaoslace - { - if (card->target){ - card->target->setColor(Constants::MTG_COLOR_RED, 1); - }else{ - Spell * starget = spell->getNextSpellTarget(); - starget->source->setColor(Constants::MTG_COLOR_RED, 1); - } - break; - } - case 1335: //Circle of protection : black - { - game->addObserver(NEW ACircleOfProtection( _id,card, Constants::MTG_COLOR_BLACK)); - break; - } - case 1336: //Circle of protection : blue - { - game->addObserver(NEW ACircleOfProtection( _id,card, Constants::MTG_COLOR_BLUE)); - break; - } - case 1337: //Circle of protection : green - { - game->addObserver(NEW ACircleOfProtection( _id,card, Constants::MTG_COLOR_GREEN)); - break; - } - case 1338: //Circle of protection : red - { - game->addObserver(NEW ACircleOfProtection( _id,card, Constants::MTG_COLOR_RED)); - break; - } - case 1339: //Circle of protection : white - { - game->addObserver(NEW ACircleOfProtection( _id,card, Constants::MTG_COLOR_WHITE)); - break; - } - case 1101: //clockwork Beast - { - game->addObserver(NEW AClockworkBeast(_id,card)); - break; - } - case 1102: //Conservator - { - game->addObserver(NEW AConservator(_id,card)); - break; - } - - case 1197: //Creature Bond - { - game->addObserver(NEW ACreatureBond(_id,card, card->target)); - break; - } - case 1103: //Crystal Rod - { - int cost[] = {Constants::MTG_COLOR_BLUE, 1}; - ASpellCastLife* ability = NEW ASpellCastLife(_id, card, Constants::MTG_COLOR_WHITE,NEW ManaCost(cost,1) , 1); - game->addObserver(ability); - break; - } - case 1152: //Deathlace - { - if (card->target){ - card->target->setColor(Constants::MTG_COLOR_BLACK, 1); - }else{ - Spell * starget = spell->getNextSpellTarget(); - starget->source->setColor(Constants::MTG_COLOR_BLACK, 1); - } - break; - } - case 1106: //Disrupting Scepter - { - ADisruptingScepter * ability = NEW ADisruptingScepter(_id,card); - game->addObserver(ability); - break; - } - case 1284: //Dragon Whelp - { - game->addObserver(NEW ADragonWhelp(_id,card)); - break; - } - case 1108: //Ebony Horse - { - AEbonyHorse * ability = NEW AEbonyHorse(_id,card); - game->addObserver(ability); - break; - } - case 1345: //Farmstead - { - game->addObserver(NEW AFarmstead(_id, card,card->target)); - break; - } - case 1291: //Fireball - { - int x = computeX(spell,card); - game->addObserver(NEW AFireball(_id, card,spell, x)); - break; - } - case 1245: //Force of Nature - { - game->addObserver(NEW AForceOfNature(_id,card)); - break; - } - case 1110: //Glasses Of Urza - { - AGlassesOfUrza * ability = NEW AGlassesOfUrza(_id,card); - game->addObserver(ability); - break; - } - case 1112: //Howling Mine - { - game->addObserver(NEW AHowlingMine(_id, card)); - break; - } - case 1252: //Instill Energy - { - game->addObserver(NEW AUntaperOnceDuringTurn(_id, card, card->target, NEW ManaCost())); - break; - } - case 1113: //Iron Star - { - int cost[] = {Constants::MTG_COLOR_ARTIFACT, 1}; - ASpellCastLife* ability = NEW ASpellCastLife(_id, card, Constants::MTG_COLOR_RED,NEW ManaCost(cost,1) , 1); - game->addObserver(ability); - break; - } - case 1351: // Island Sanctuary - { - game->addObserver(NEW AIslandSanctuary(_id, card)); - break; - } - case 1114: //Ivory cup - { - int cost[] = {Constants::MTG_COLOR_ARTIFACT, 1}; - ASpellCastLife* ability = NEW ASpellCastLife(_id, card, Constants::MTG_COLOR_WHITE,NEW ManaCost(cost,1) , 1); - game->addObserver(ability); - break; - } - case 1115: //Ivory Tower - { - game->addObserver(NEW ALifeZoneLink(_id ,card, Constants::MTG_PHASE_UPKEEP, 4, 1, 1)); - break; - } - case 1117: //Jandors Ring - { - game->addObserver(NEW AJandorsRing( _id, card)); - break; - } - case 1121: //Kormus Bell - { - game->addObserver(NEW AConvertLandToCreatures(id, card, "swamp")); - break; - } - case 1254: //Kudzu - { - game->addObserver(NEW AKudzu(id, card, card->target)); - break; - } - case 1257: //Lifelace - { - if (card->target){ - card->target->setColor(Constants::MTG_COLOR_GREEN, 1); - }else{ - Spell * starget = spell->getNextSpellTarget(); - starget->source->setColor(Constants::MTG_COLOR_GREEN, 1); - } - break; - } - - case 1259: //Living lands - { - game->addObserver(NEW AConvertLandToCreatures(id, card, "forest")); - break; - } - case 1124: //Mana Vault - { - int output[] = {Constants::MTG_COLOR_ARTIFACT, 3}; - game->addObserver(NEW AManaProducer(_id,card,card,NEW ManaCost(output,1))); - int cost[] = {Constants::MTG_COLOR_ARTIFACT, 4}; - game->addObserver(NEW AUntapManaBlocker(_id+1, card, NEW ManaCost(cost,1))); - game->addObserver(NEW ARegularLifeModifierAura(_id+2, card, card, Constants::MTG_PHASE_DRAW, -1, 1)); - break; - } - case 1215: //Power Leak - { - game->addObserver( NEW APowerLeak(_id ,card, card->target)); - break; - } - case 1358: //Purelace - { - if (card->target){ - card->target->setColor(Constants::MTG_COLOR_WHITE, 1); - }else{ - Spell * starget = spell->getNextSpellTarget(); - starget->source->setColor(Constants::MTG_COLOR_WHITE, 1); - } - break; - } - case 1312: //Red Elemental Blast - { - if (card->target){ - card->target->controller()->game->putInGraveyard(card->target); - }else{ - Spell * starget = spell->getNextSpellTarget(); - game->mLayers->stackLayer()->Fizzle(starget); - } - break; - } - case 1136: //Soul Net - { - game->addObserver( NEW ASoulNet(_id ,card)); - break; - } - case 1139: //The Rack - { - game->addObserver( NEW ALifeZoneLink(_id ,card, Constants::MTG_PHASE_UPKEEP, -3)); - break; - } - case 1140: //Throne of Bone - { - int cost[] = {Constants::MTG_COLOR_ARTIFACT, 1}; - ASpellCastLife* ability = NEW ASpellCastLife(_id, card, Constants::MTG_COLOR_BLACK,NEW ManaCost(cost,1) , 1); - game->addObserver(ability); - break; - } - - case 1142: //Wooden Sphere - { - int cost[] = {Constants::MTG_COLOR_ARTIFACT, 1}; - ASpellCastLife* ability = NEW ASpellCastLife(_id, card, Constants::MTG_COLOR_GREEN,NEW ManaCost(cost,1) , 1); - game->addObserver(ability); - break; - } - case 1143: //Animate Dead - { - AAnimateDead * a = NEW AAnimateDead(_id, card, card->target); - game->addObserver(a); - card->target = ((MTGCardInstance * )a->target); - break; - } - case 1156: //Drain Life - { - Damageable * target = spell->getNextDamageableTarget(); - int x = spell->cost->getConvertedCost() - 2; //TODO Fix that !!! + X should be only black mana, that needs to be checked ! - game->mLayers->stackLayer()->addDamage(card, target, x); - if (target->life < x) x = target->life; - game->currentlyActing()->life+=x; - break; - } - case 1159: //Erg Raiders - { - AErgRaiders* ability = NEW AErgRaiders(_id, card); - game->addObserver(ability); - break; - } - case 1202: //Hurkyl's Recall - { - Player * player = spell->getNextPlayerTarget(); - if (player){ - for (int i = 0; i < 2; i++){ - MTGInPlay * inplay = game->players[i]->game->inPlay; - for (int j= inplay->nb_cards -1 ; j >=0 ; j--){ - MTGCardInstance * card = inplay->cards[j]; - if (card->owner == player && card->hasType("artifact")){ - player->game->putInZone(card, inplay, player->game->hand); - } - } - } - } - break; - } - case 1165: //Hypnotic Specter - { - game->addObserver(NEW AHypnoticSpecter( _id, card)); - break; - } - case 1258: //Living Artifact - { - game->addObserver(NEW ALivingArtifact( _id, card, card->target)); - break; - } - case 1166: //Lord Of The Pit - { - game->addObserver(NEW ALordOfThePit( _id, card)); - break; - } - case 1209: //Mana Short - { - Player * player = spell->getNextPlayerTarget(); - if (player){ - MTGInPlay * inplay = player->game->inPlay; - for (int i = 0; i < inplay->nb_cards; i++){ - MTGCardInstance * current = inplay->cards[i]; - if (current->hasType(Subtypes::TYPE_LAND)) current->tap(); - } - player->getManaPool()->init(); - } - break; - } - case 1167: //Mind Twist - { - int xCost = computeX(spell,card); - for (int i = 0; i < xCost; i++){ - game->opponent()->game->discardRandom(game->opponent()->game->hand); - } - break; - } - case 1171: //Paralyze - { - int cost[] = {Constants::MTG_COLOR_ARTIFACT, 4}; - game->addObserver(NEW AUntapManaBlocker(_id, card,card->target, NEW ManaCost(cost,1))); - card->target->tap(); - break; - } - case 1172: //Pestilence - { - game->addObserver(NEW APestilence(_id, card)); - break; - } - - case 1176: //Sacrifice - { - ASacrifice * ability = NEW ASacrifice(_id, card, card->target); - game->addObserver(ability); - break; - } - case 1224: //Spell Blast - { - int x = computeX(spell,card); - Spell * starget = spell->getNextSpellTarget(); - if (starget){ - if (starget->cost->getConvertedCost() <= x) game->mLayers->stackLayer()->Fizzle(starget); - } - break; - } - case 1192: //BrainGeyser - { - Player * player = spell->getNextPlayerTarget(); - int x = spell->cost->getConvertedCost() - 2; - for (int i = 0; i < x ; i++){ - player->game->drawFromLibrary(); - } - break; - } - case 1194: //Control Magic - { - game->addObserver(NEW AControlStealAura(_id, card, card->target)); - break; - } - case 1218: //Psychic Venom - { - game->addObserver(NEW APsychicVenom(_id, card, card->target)); - break; - } - case 1226: //Steal Artifact - { - game->addObserver( NEW AControlStealAura(_id, card, card->target)); - break; - } - case 1235: //Aspect of Wolf - { - game->addObserver(NEW AAspectOfWolf(_id, card, card->target)); - break; - } - case 1240: //Crumble - { - card->target->controller()->life+= card->target->getManaCost()->getConvertedCost(); - break; - } - case 1251: //Hurricane - { - int x = spell->cost->getConvertedCost() - 1; - for (int i = 0; i < 2 ; i++){ - game->mLayers->stackLayer()->addDamage(card, game->players[i], x); - for (int j = 0; j < game->players[i]->game->inPlay->nb_cards; j++){ - MTGCardInstance * current = game->players[i]->game->inPlay->cards[j]; - if (current->basicAbilities[Constants::FLYING] && current->isCreature()){ - game->mLayers->stackLayer()->addDamage(card, current, x); - } - } - } - break; - } - case 1262: //Regeneration - { - int cost[] = {Constants::MTG_COLOR_GREEN, 1}; - game->addObserver(NEW AStandardRegenerate(_id,card,card->target,NEW ManaCost(cost,1))); - break; - } - - case 1266: //stream of life - { - int x = computeX(spell,card); - spell->getNextPlayerTarget()->life += x; - break; - } - - - case 1231: //Volcanic Eruption - { - int x = computeX(spell,card); - int _x = x; - MTGCardInstance * target = spell->getNextCardTarget(); - while(target && _x){ - target->destroy(); - _x--; - target = spell->getNextCardTarget(target); - } - x-=_x; - for (int i = 0; i < 2 ; i++){ - game->mLayers->stackLayer()->addDamage(card, game->players[i], x); - for (int j = 0; j < game->players[i]->game->inPlay->nb_cards; j++){ - MTGCardInstance * current = game->players[i]->game->inPlay->cards[j]; - if (current->isCreature()){ - game->mLayers->stackLayer()->addDamage(card, current, x); - } - } - } - break; - } - - case 1285: //Dwarven Warriors - { - CreatureTargetChooser * tc = NEW CreatureTargetChooser(card); - tc->maxpower = 2; - game->addObserver(NEW ABasicAbilityModifierUntilEOT(_id, card, Constants::UNBLOCKABLE, NULL,tc)); - break; - } - case 1288: //EarthBind - { - game->addObserver(NEW AEarthbind(_id, card, card->target)); - break; - } - case 1289: //earthquake - { - int x = computeX(spell,card); - for (int i = 0; i < 2 ; i++){ - game->mLayers->stackLayer()->addDamage(card, game->players[i], x); - for (int j = 0; j < game->players[i]->game->inPlay->nb_cards; j++){ - MTGCardInstance * current = game->players[i]->game->inPlay->cards[j]; - if (!current->basicAbilities[Constants::FLYING] && current->isCreature()){ - game->mLayers->stackLayer()->addDamage(card, current, x); - } - } - } - break; - } - case 1344: //Eye for an Eye - { - Damage * damage = spell->getNextDamageTarget(); - if (damage){ - game->mLayers->stackLayer()->addDamage(card,damage->source->controller(),damage->damage); - } - break; - } - case 1243: //Fastbond - { - game->addObserver(NEW AFastbond(_id, card)); - break; - } - case 1238: //Cockatrice - { - game->addObserver(NEW AOldSchoolDeathtouch(_id,card)); - break; - } - case 1362: //Reverse polarity - { - ActionStack * as = game->mLayers->stackLayer(); - Player * controller = card->controller(); - Damage * current = ((Damage *)as->getNext(NULL,ACTION_SPELL, RESOLVED_OK)); - while(current){ - if (current->target == controller && current->source->hasType("artifact")){ - controller->life+= current->damage * 2; - } - current = ((Damage *)as->getNext(current,ACTION_SPELL, RESOLVED_OK)); - } - break; - } - case 1225: //Stasis - { - game->addObserver(NEW AStasis(_id, card)); - break; - } - - case 1367: //Swords to Plowshares - { - Player * p = card->target->controller(); - p->life+= card->target->power; - p->game->putInZone(card->target,p->game->inPlay,card->owner->game->removedFromGame); - break; - } - case 1267: //Thicket Basilic - { - game->addObserver(NEW AOldSchoolDeathtouch(_id,card)); - break; - } - case 1227: //Toughtlace - { - if (card->target){ - card->target->setColor(Constants::MTG_COLOR_BLUE, 1); - }else{ - Spell * starget = spell->getNextSpellTarget(); - starget->source->setColor(Constants::MTG_COLOR_BLUE, 1); - } - break; - } - - //Addons Legends - case 1427: //Abomination (does not work make the game crash) - { - game->addObserver(NEW AAbomination(_id,card)); - break; - } - case 1533: //Livingplane - { - game->addObserver(NEW AConvertLandToCreatures(id, card, "land")); - break; - } - case 1607: //Divine Offering - { - card->target->controller()->game->putInGraveyard(card->target); - game->currentlyActing()->life+= card->target->getManaCost()->getConvertedCost(); - break; - } - case 1480: //Energy Tap - { - card->target->tap(); - int mana = card->target->getManaCost()->getConvertedCost(); - game->currentlyActing()->getManaPool()->add(Constants::MTG_COLOR_ARTIFACT, mana); - } - - case 1614: // Great Defender - { - int toughness = card->target->getManaCost()->getConvertedCost(); - int power = 0; - game->addObserver(NEW AInstantPowerToughnessModifierUntilEOT(id, card, card->target, NEW WParsedPT(power,toughness))); - } - - case 1703: //Pendelhaven - { - CreatureTargetChooser * tc = NEW CreatureTargetChooser(card); - tc->maxpower = 1; - tc->maxtoughness =1; - game->addObserver(NEW ATargetterPowerToughnessModifierUntilEOT(id, card, NEW WParsedPT(1,2), NEW ManaCost(),tc)); - break; - } - - //Addons ICE-AGE Cards - - case 2660: //Word of Blasting - { - card->target->controller()->life-= card->target->getManaCost()->getConvertedCost(); - break; - } - case 2474: //Minion of Leshrac - { - game->addObserver(NEW AMinionofLeshrac( _id, card)); - break; - } - case 2421: //Shield of the Age - { - game->addObserver(NEW AShieldOfTheAge( _id, card)); - break; - } - case 2435: //Whalebone Glider - { - int cost[] = {Constants::MTG_COLOR_ARTIFACT,2}; - CreatureTargetChooser * tc = NEW CreatureTargetChooser(card); - tc->maxpower = 3; - game->addObserver(NEW ABasicAbilityModifierUntilEOT(_id, card, Constants::FLYING, NEW ManaCost(cost,1),tc)); - break; - } - case 2393: //Aegis of the Meek work but work also for 0/1 creatures... :D - { - int cost[] = {Constants::MTG_COLOR_ARTIFACT,1}; - CreatureTargetChooser * tc = NEW CreatureTargetChooser(card); - tc->maxpower = 1; - tc->maxtoughness =1; - game->addObserver(NEW ATargetterPowerToughnessModifierUntilEOT(id, card, NEW WParsedPT(1,2), NEW ManaCost(cost,1),tc)); - break; - } - -//---addon Alliance--- - - case 3194: // Exile - { - game->currentlyActing()->life+=card->target->toughness; - break; - } - -// --- addon Mirage --- - - case 3410: //Seed of Innocence - { - GameObserver * game = GameObserver::GetInstance(); - for (int i = 0; i < 2 ; i++){ - for (int j = 0; j < game->players[i]->game->inPlay->nb_cards; j++){ - MTGCardInstance * current = game->players[i]->game->inPlay->cards[j]; - if (current->hasType("Artifact")){ - game->players[i]->game->putInGraveyard(current); - current->controller()->life+= current->getManaCost()->getConvertedCost(); - } - } - } - break; - } - -//-- addon 10E--- - - case 129710: //Angelic Chorus - { - game->addObserver( NEW AAngelicChorus(_id,card)); - break; - } - - case 129767: //Threaten - { - game->addObserver( NEW AInstantControlSteal(_id,card,card->target)); - break; - } - case 130373: //Lavaborn Muse - { - game->addObserver( NEW ALavaborn(_id ,card, Constants::MTG_PHASE_UPKEEP, -3,-3)); - break; - } - case 129774: // Traumatize - { - int nbcards; - Player * player = spell->getNextPlayerTarget(); - MTGLibrary * library = player->game->library; - nbcards = (library->nb_cards)/2; - for (int i = 0; i < nbcards; i++){ - if (library->nb_cards) - player->game->putInZone(library->cards[library->nb_cards-1],library, player->game->graveyard); - } - break; - } - - - case 135215: //Sylvan Basilisk - { - game->addObserver( NEW ABasilik (_id ,card)); - break; - } - case 130553:// Beacon of Immortality - { - Player * player = spell->getNextPlayerTarget(); - if (player->life < (INT_MAX / 4) ) player->life += player->life; - zones->putInZone(card,spell->from,zones->library); - zones->library->shuffle(); - break; - } - case 135262:// Beacon of Destruction & unrest - { - zones->putInZone(card,spell->from,zones->library); - zones->library->shuffle(); - break; - } - case 129750: //Sudden Impact - { - Damageable * target = spell->getNextDamageableTarget(); - Player * p = spell->getNextPlayerTarget(); - MTGHand * hand = p->game->hand; - int damage = hand->nb_cards; - game->mLayers->stackLayer()->addDamage(card, target, damage); - break; - } - case 130369: // Soulblast - { - int damage = 0; - Damageable * target = spell->getNextDamageableTarget(); - for (int j = card->controller()->game->inPlay->nb_cards-1; j >=0 ; --j){ - MTGCardInstance * current = card->controller()->game->inPlay->cards[j]; - if (current->hasType(Subtypes::TYPE_CREATURE)){ - card->controller()->game->putInGraveyard(current); - damage+= current->power; - } - } - game->mLayers->stackLayer()->addDamage(card, target, damage); - break; - } - - - case 129698: // Reminisce - { - int nbcards; - Player * player = spell->getNextPlayerTarget(); - MTGLibrary * library = player->game->library; - MTGGraveyard * graveyard = player->game->graveyard; - nbcards = (graveyard->nb_cards); - for (int i = 0; i < nbcards; i++){ - if (graveyard->nb_cards) - player->game->putInZone(graveyard->cards[graveyard->nb_cards-1],graveyard, library); - } - library->shuffle(); - break; - } - - -// --- addon Invasion--- - case 23195: //Artifact Mutation - { - card->target->controller()->game->putInGraveyard(card->target); - int x = card->target->getManaCost()->getConvertedCost(); - ATokenCreator * tok = NEW ATokenCreator(id,card,NEW ManaCost(),"Saproling","creature Saproling",1,1,"green",0); - for (int i=0; i < x; i++){ - tok->resolve(); - } - delete(tok); - break; - } - -// --- addon Ravnica--- - - case 89114: //Psychic Drain - { - Player * player = spell->getNextPlayerTarget(); - MTGLibrary * library = player->game->library; - int x = computeX(spell,card); - for (int i = 0; i < x; i++){ - if (library->nb_cards) - player->game->putInZone(library->cards[library->nb_cards-1],library, player->game->graveyard); - } - game->currentlyActing()->life+= x; - break; - } - - // --- addon ARB--- - case 179614: // Morbid Bloom - { - int x = card->target->toughness; - ATokenCreator * tok = NEW ATokenCreator(id,card,NEW ManaCost(),"Saproling","creature Saproling",1,1,"green",0); - for (int i=0; i < x; i++){ - tok->resolve(); - } - delete(tok); - break; - } - - default: - break; - } - - - - - /* We want to get rid of these basicAbility things. - * basicAbilities themselves are alright, but creating New object depending on them is dangerous - * The main reason is that classes that add an ability to a card do NOT create these objects, and therefore do NOT - * Work. - * For example, setting EXALTED for a creature is not enough right now... - * It shouldn't be necessary to add an object. State based abilities could do the trick - */ - - if (card->basicAbilities[Constants::EXALTED]){ - game->addObserver(NEW AExalted(_id, card)); - } - - // Tested works the first r10 did not function because of the mistake in the array of the definition - if (card->basicAbilities[Constants::FORESTHOME]){ - game->addObserver(NEW AStrongLandLinkCreature(_id, card, "forest")); - } - if (card->basicAbilities[Constants::ISLANDHOME]){ - game->addObserver(NEW AStrongLandLinkCreature(_id, card, "island")); - } - if (card->basicAbilities[Constants::MOUNTAINHOME]){ - game->addObserver(NEW AStrongLandLinkCreature(_id, card,"moutain")); - } - if (card->basicAbilities[Constants::SWAMPHOME]){ - game->addObserver(NEW AStrongLandLinkCreature(_id, card,"swamp")); - } - if (card->basicAbilities[Constants::PLAINSHOME]){ - game->addObserver(NEW AStrongLandLinkCreature(_id, card,"plains")); - } - - if (card->hasType("instant") || card->hasType("sorcery")){ - MTGPlayerCards * zones = card->controller()->game; - zones->putInZone(card,zones->stack,zones->graveyard); - } - - -} - -MTGAbility::MTGAbility(int id, MTGCardInstance * card):ActionElement(id){ - game = GameObserver::GetInstance(); - source = card; - target = card; - aType = MTGAbility::UNKNOWN; - cost = NULL; - forceDestroy = 0; - oneShot = 0; -} - -MTGAbility::MTGAbility(int id, MTGCardInstance * _source,Targetable * _target ):ActionElement(id){ - game = GameObserver::GetInstance(); - source = _source; - target = _target; - aType = MTGAbility::UNKNOWN; - cost = NULL; - forceDestroy = 0; - oneShot = 0; -} - -int MTGAbility::stillInUse(MTGCardInstance * card){ - if (card==source || card==target) return 1; - return 0; -} - -MTGAbility::~MTGAbility(){ - if (!isClone){ - SAFE_DELETE(cost); - } -} - -int MTGAbility::addToGame(){ - GameObserver::GetInstance()->addObserver(this); - return 1; -} - -int MTGAbility::removeFromGame(){ - GameObserver::GetInstance()->removeObserver(this); - return 1; -} - -//returns 1 if this ability needs to be removed from the list of active abilities -int MTGAbility::testDestroy(){ - if (game->mLayers->stackLayer()->has(this)) return 0; - if (waitingForAnswer) return 0; - if (forceDestroy == 1) return 1; - if (forceDestroy == -1) return 0; - if (!game->isInPlay(source) ) return 1; - if (target && !game->isInPlay((MTGCardInstance *)target)) return 1; - return 0; -} - - - -int MTGAbility::fireAbility(){ - game->mLayers->stackLayer()->addAbility(this); - return 1; -} - -ostream& MTGAbility::toString(ostream& out) const -{ - return out << "MTGAbility ::: menuText : " << menuText - << " ; game : " << game - << " ; forceDestroy : " << forceDestroy - << " ; cost : " << cost - << " ; target : " << target - << " ; aType : " << aType - << " ; source : " << source; -} - -// - -ActivatedAbility::ActivatedAbility(int id, MTGCardInstance * card, ManaCost * _cost, int _playerturnonly,int tap):MTGAbility(id,card), playerturnonly(_playerturnonly), needsTapping(tap){ - cost = _cost; -} - - -int ActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ - Player * player = game->currentPlayer; - if (!playerturnonly) player = game->currentlyActing(); - if (card == source && source->controller()==player && player==game->currentlyActing() && (!needsTapping || (!source->isTapped() && !source->hasSummoningSickness()))){ - if (!cost) return 1; - if (!mana) mana = player->getManaPool(); - if (!mana->canAfford(cost)) return 0; - return 1; - } - return 0; -} - -int ActivatedAbility::reactToClick(MTGCardInstance * card){ - if (!isReactingToClick(card)) return 0; - if (cost){ - cost->setExtraCostsAction(this, card); - if (!cost->isExtraPaymentSet()){ - game->waitForExtraPayment = cost->extraCosts; - return 0; - } - game->currentlyActing()->getManaPool()->pay(cost); - cost->doPayExtra(); - } - if (needsTapping && source->isInPlay()) source->tap(); - fireAbility(); - - return 1; - -} - -int ActivatedAbility::reactToTargetClick(Targetable * object){ - if (!isReactingToTargetClick(object)) return 0; - if (cost){ - if (object->typeAsTarget() == TARGET_CARD) cost->setExtraCostsAction(this, (MTGCardInstance *) object); - if (!cost->isExtraPaymentSet()){ - game->waitForExtraPayment = cost->extraCosts; - return 0; - } - game->currentlyActing()->getManaPool()->pay(cost); - cost->doPayExtra(); - } - if (needsTapping && source->isInPlay()) source->tap(); - fireAbility(); - return 1; - -} - -ostream& ActivatedAbility::toString(ostream& out) const -{ - out << "ActivatedAbility ::: playerturnonly : " << playerturnonly - << " ; needsTapping : " << needsTapping - << " ("; - return MTGAbility::toString(out) << ")"; -} - - -//The whole targetAbility mechanism is messed up, mainly because of its interactions with -// the ActionLayer, GameObserver, and parent class ActivatedAbility. -// Currently choosing a target is a complete different mechanism for put into play and for other abilities. -// It probably shouldn't be the case. - -TargetAbility::TargetAbility(int id, MTGCardInstance * card, TargetChooser * _tc,ManaCost * _cost, int _playerturnonly,int tap):ActivatedAbility(id, card,_cost,_playerturnonly, tap){ - tc = _tc; - ability = NULL; -} - -TargetAbility::TargetAbility(int id, MTGCardInstance * card,ManaCost * _cost, int _playerturnonly,int tap):ActivatedAbility(id, card,_cost,_playerturnonly, tap){ - tc = NULL; - ability = NULL; -} - - -int TargetAbility::reactToTargetClick(Targetable * object){ - if (object->typeAsTarget() == TARGET_CARD) return reactToClick((MTGCardInstance *)object); - if (waitingForAnswer){ - if (tc->toggleTarget(object) == TARGET_OK_FULL){ - waitingForAnswer = 0; - game->mLayers->actionLayer()->setCurrentWaitingAction(NULL); - return ActivatedAbility::reactToClick(source); - } - return 1; - } - return 0; -} - - -int TargetAbility::reactToClick(MTGCardInstance * card){ - if (!waitingForAnswer) { - if (isReactingToClick(card)){ - waitingForAnswer = 1; - game->mLayers->actionLayer()->setCurrentWaitingAction(this); - tc->initTargets(); - return 1; - } - }else{ - if (card == source && (tc->targetsReadyCheck() == TARGET_OK || tc->targetsReadyCheck() == TARGET_OK_FULL)){ - waitingForAnswer = 0; - game->mLayers->actionLayer()->setCurrentWaitingAction(NULL); - return ActivatedAbility::reactToClick(source); - }else{ - if (tc->toggleTarget(card) == TARGET_OK_FULL){ - int result = ActivatedAbility::reactToClick(source); - if (result) { - waitingForAnswer = 0; - game->mLayers->actionLayer()->setCurrentWaitingAction(NULL); - } - return result; - } - return 1; - } - } - return 0; -} - -void TargetAbility::Render(){ - //TODO ? -} - - -int TargetAbility::resolve(){ - Targetable * t = tc->getNextTarget(); - if (t && ability){ - ability->target = t; - return ability->resolve(); - } - return 0; -} - -const char * TargetAbility::getMenuText(){ - if (ability) return ability->getMenuText(); - return ActivatedAbility::getMenuText(); -} - -TargetAbility::~TargetAbility(){ - if (!isClone) SAFE_DELETE(ability); -} - -ostream& TargetAbility::toString(ostream& out) const -{ - out << "TargetAbility ::: ("; - return ActivatedAbility::toString(out) << ")"; -} - -// - - -TriggeredAbility::TriggeredAbility(int id, MTGCardInstance * card, Targetable * _target):MTGAbility(id,card, _target){ -} - - -TriggeredAbility::TriggeredAbility(int id, MTGCardInstance * card):MTGAbility(id,card){ -} - -int TriggeredAbility::receiveEvent(WEvent * e){ - if (triggerOnEvent(e)){ - fireAbility(); - return 1; - } - return 0; -} - -void TriggeredAbility::Update(float dt){ - if (trigger()) fireAbility(); -} - -ostream& TriggeredAbility::toString(ostream& out) const -{ - out << "TriggeredAbility ::: ("; - return MTGAbility::toString(out) << ")"; -} - - -// -InstantAbility::InstantAbility(int _id, MTGCardInstance * source):MTGAbility(_id, source){ - init = 0; -} - -void InstantAbility::Update(float dt){ - if (!init){ - init = resolve(); - } -} - -InstantAbility::InstantAbility(int _id, MTGCardInstance * source, Damageable * _target):MTGAbility(_id, source, _target){ - init = 0; -} - - - -//Instant abilities last generally until the end of the turn -int InstantAbility::testDestroy(){ - int newPhase = game->getCurrentGamePhase(); - if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_AFTER_EOT) return 1; - currentPhase = newPhase; - return 0; - -} - -ostream& InstantAbility::toString(ostream& out) const -{ - out << "InstantAbility ::: init : " << init - << " ("; - return MTGAbility::toString(out) << ")"; -} - -bool ListMaintainerAbility::canTarget(MTGGameZone * zone){ - if (tc) return tc->targetsZone(zone); - for (int i = 0; i < 2; i++){ - Player * p = game->players[i]; - if (zone == p->game->inPlay) return true; - } - return false; -} - -void ListMaintainerAbility::updateTargets(){ - //remove invalid ones - map temp; - for (map::iterator it=cards.begin(); it != cards.end(); ++it){ - MTGCardInstance * card = (*it).first; - if (!canBeInList(card)) temp[card] = true; - } - - for (map::iterator it=temp.begin(); it != temp.end(); ++it){ - MTGCardInstance * card = (*it).first; - cards.erase(card); - removed(card); - } - - temp.clear(); - - //add New valid ones - for (int i = 0; i < 2; i++){ - Player * p = game->players[i]; - MTGGameZone * zones[] = {p->game->inPlay,p->game->graveyard,p->game->hand,p->game->library}; - for (int k = 0; k < 4; k++){ - MTGGameZone * zone = zones[k]; - if (canTarget(zone)){ - for (int j = 0; j < zone->nb_cards; j++){ - if (canBeInList(zone->cards[j])){ - if(cards.find(zone->cards[j]) == cards.end()){ - temp[zone->cards[j]] = true; - } - } - } - } - } - } - - for (map::iterator it=temp.begin(); it != temp.end(); ++it){ - MTGCardInstance * card = (*it).first; - cards[card] = true; - added(card); - } - - temp.clear(); - - for (int i = 0; i < 2; ++i){ - Player * p = game->players[i]; - if (!players[p] && canBeInList(p)){ - players[p] = true; - added(p); - }else if (players[p] && !canBeInList(p)){ - players[p] = false; - removed(p); - } - } - -} - -void ListMaintainerAbility::Update(float dt){ - updateTargets(); -} - -//Destroy the spell -> remove all targets -int ListMaintainerAbility::destroy(){ - map::iterator it = cards.begin(); - - while ( it!=cards.end()){ - MTGCardInstance * card = (*it).first; - cards.erase(card); - removed(card); - it = cards.begin(); - } - return 1; -} - -ostream& ListMaintainerAbility::toString(ostream& out) const -{ - out << "ListMaintainerAbility ::: ("; - return MTGAbility::toString(out) << ")"; -} - - - -TriggerAtPhase::TriggerAtPhase(int id, MTGCardInstance * source, Targetable * target,int _phaseId, int who):TriggeredAbility(id, source,target),phaseId(_phaseId),who(who){ - GameObserver * g = GameObserver::GetInstance(); - newPhase = g->getCurrentGamePhase(); - currentPhase = newPhase; -} - -int TriggerAtPhase::trigger(){ - GameObserver * g = GameObserver::GetInstance(); - int result = 0; - if (currentPhase != newPhase && newPhase == phaseId){ - result = 0; - switch(who){ - case 1: - if(g->currentPlayer == source->controller()) result = 1; - break; - case -1: - if(g->currentPlayer != source->controller()) result = 1; - break; - case -2: - if(source->target) { - if (g->currentPlayer == source->target->controller()) result = 1; - }else { - if(g->currentPlayer == source->controller()) result = 1; - } - break; - default: - result = 1; - break; - } - } - return result; -} - -TriggerAtPhase* TriggerAtPhase::clone() const{ - TriggerAtPhase * a = NEW TriggerAtPhase(*this); - a->isClone = 1; - return a; -} - -TriggerNextPhase::TriggerNextPhase(int id, MTGCardInstance * source, Targetable * target,int _phaseId,int who):TriggerAtPhase(id, source,target,_phaseId, who){ - destroyActivated = 0; -} - -int TriggerNextPhase::testDestroy(){ - if (newPhase <= phaseId) destroyActivated = 1; - if ( newPhase > phaseId && destroyActivated){ - return 1; - } - return 0; -} - -TriggerNextPhase* TriggerNextPhase::clone() const{ - TriggerNextPhase * a = NEW TriggerNextPhase(*this); - a->isClone = 1; - return a; -} - -GenericTriggeredAbility::GenericTriggeredAbility(int id, MTGCardInstance * _source, TriggeredAbility * _t, MTGAbility * a , MTGAbility * dc, Targetable * _target ): TriggeredAbility(id, _source,_target){ - if (!target) target = source; - t = _t; - ability = a; - destroyCondition = dc; - - t->source = source; - t->target = target; - ability->source = source; - ability->target = target; - if (destroyCondition){ - destroyCondition->source = source; - destroyCondition->target = target;; - } -} - -int GenericTriggeredAbility::trigger(){ - return t->trigger(); -} - - -int GenericTriggeredAbility::triggerOnEvent(WEvent * e){ - return t->triggerOnEvent(e); -} - -void GenericTriggeredAbility::Update(float dt){ - GameObserver * g = GameObserver::GetInstance(); - int newPhase = g->getCurrentGamePhase(); - t->newPhase = newPhase; - TriggeredAbility::Update(dt); - t->currentPhase = newPhase; -} - -int GenericTriggeredAbility::resolve(){ - if (ability->oneShot) return ability->resolve(); - MTGAbility * clone = ability->clone(); - clone->addToGame(); - return 1; -} - -int GenericTriggeredAbility::testDestroy(){ - if (!TriggeredAbility::testDestroy()) return 0; - if (destroyCondition) return (destroyCondition->testDestroy()); - return t->testDestroy(); -} - -GenericTriggeredAbility::~GenericTriggeredAbility(){ - if (!isClone){ - delete t; - delete ability; - SAFE_DELETE(destroyCondition); - } -} - - const char * GenericTriggeredAbility::getMenuText(){ - return ability->getMenuText(); - } - -GenericTriggeredAbility* GenericTriggeredAbility::clone() const{ - GenericTriggeredAbility * a = NEW GenericTriggeredAbility(*this); - a->isClone = 1; - return a; -} - -/*Mana Producers (lands) -//These have a reactToClick function, and therefore two manaProducers on the same card conflict with each other -//That means the player has to choose one. although that is perfect for cards such as birds of paradise or badlands, -other solutions need to be provided for abilities that add mana (ex: mana flare) -*/ - - -AManaProducer::AManaProducer(int id, MTGCardInstance * card, Targetable * t, ManaCost * _output, ManaCost * _cost , int doTap, int who):ActivatedAbilityTP(id, card,t,_cost,doTap,who){ - - LOG("==Creating ManaProducer Object"); - aType = MTGAbility::MANA_PRODUCER; - cost = _cost; - output = _output; - - menutext = ""; - - - - LOG("==ManaProducer Object Creation successful !"); -} - - int AManaProducer::isReactingToClick(MTGCardInstance * _card, ManaCost * mana){ - int result = 0; - if (!mana) mana = game->currentlyActing()->getManaPool(); - if (_card == source && (!tap || !source->isTapped()) && game->currentlyActing()->game->inPlay->hasCard(source) && (source->hasType(Subtypes::TYPE_LAND) || !tap || !source->hasSummoningSickness()) ){ - if (!cost || mana->canAfford(cost)) result = 1; - } - return result; - } - - int AManaProducer::resolve(){ +#include "../include/config.h" +#include "../include/MTGAbility.h" +#include "../include/ManaCost.h" +#include "../include/MTGGameZones.h" +#include "../include/AllAbilities.h" +#include "../include/Damage.h" +#include "../include/TargetChooser.h" +#include "../include/CardGui.h" +#include "../include/MTGDeck.h" +#include "../include/Blocker.h" +#include "../include/Translate.h" + + +int AbilityFactory::countCards(TargetChooser * tc, Player * player, int option){ + int result = 0; + GameObserver * game = GameObserver::GetInstance(); + for (int i = 0; i < 2 ; i++){ + if (player && player!= game->players[i]) continue; + MTGGameZone * zones[] = {game->players[i]->game->inPlay,game->players[i]->game->graveyard,game->players[i]->game->hand}; + for (int k = 0; k < 3; k++){ + for (int j = zones[k]->nb_cards-1; j >=0 ; j--){ + MTGCardInstance * current = zones[k]->cards[j]; + if (tc->canTarget(current)){ + switch (option){ + case COUNT_POWER: + result+= current->power; + break; + default: + result++; + break; + } + } + } + } + } + return result; +} + + + +int AbilityFactory::CantBlock(TargetChooser * tc){ + GameObserver * g = GameObserver::GetInstance(); + MTGCardInstance * source = tc->source; + for (int j = g->opponent()->game->inPlay->nb_cards-1; j >=0 ; j--){ + MTGCardInstance * current = g->opponent()->game->inPlay->cards[j]; + if (tc->canTarget(current)){ + current->canBlock(source); + return 0; + } + } + return 1; +} + + +int AbilityFactory::parsePowerToughness(string s, int *power, int *toughness){ + size_t found = s.find("/"); + 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 = -1; + + *power = atoi(s.substr(start+1,s.size()-found).c_str()); + *toughness = atoi(s.substr(found+1,end-found-1).c_str()); + + return 1; + } + return 0; +} + +TriggeredAbility * AbilityFactory::parseTrigger(string magicText, int id, Spell * spell, MTGCardInstance *card, Targetable * target){ + size_t found = magicText.find("@"); + if (found == string::npos) return NULL; + + found = magicText.find(":"); + if (found == string::npos) return NULL; + string s = magicText.substr(0,found); + + //Card Changed Zone + found = s.find("movedto("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+8,end - found - 8); + TargetChooserFactory tcf; + TargetChooser *toTc = tcf.createTargetChooser(starget,card); + toTc->targetter = NULL; + + TargetChooser *fromTc = NULL; + found = s.find("from("); + if (found != string::npos){ + end = s.find (")", found); + starget = s.substr(found+5,end - found - 5); + if (starget.find("|") == string::npos) starget.insert(0,"*|"); + fromTc = tcf.createTargetChooser(starget,card); + fromTc->targetter = NULL; + } + return NEW TrCardAddedToZone(id,card,toTc,(TargetZoneChooser *)fromTc); + } + + //Card Tapped + found = s.find("tapped("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+7,end - found - 7); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + + return NEW TrCardTapped(id,card,tc); + } + + //Card Damaging + found = s.find("damaged("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+8,end - found - 8); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + found = s.find("from("); + + TargetChooser *fromTc = NULL; + if (found != string::npos){ + end = s.find (")", found); + starget = s.substr(found+5,end - found - 5); + fromTc = tcf.createTargetChooser(starget,card); + fromTc->targetter = NULL; + } + return NEW TrDamaged(id,card,tc,fromTc); + } + + int who = 0; + if (s.find("my") != string::npos) who = 1; + if (s.find("opponent") != string::npos) who = -1; + if (s.find("targetcontroller") != string::npos) who = -2; + + //Next Time... + found = s.find("next"); + if (found != string::npos){ + for (int i = 0; i < Constants::NB_MTG_PHASES; i++){ + found = s.find(Constants::MTGPhaseCodeNames[i]); + if (found != string::npos){ + return NEW TriggerNextPhase(id, card,target,i,who); + } + } + } + + //Each Time... + found = magicText.find("each"); + if (found != string::npos){ + for (int i = 0; i < Constants::NB_MTG_PHASES; i++){ + found = magicText.find(Constants::MTGPhaseCodeNames[i]); + if (found != string::npos){ + return NEW TriggerAtPhase(id, card,target,i,who); + } + } + } + + return NULL; +} + + + + + +//Parses a string and returns the corresponding MTGAbility object +// Returns NULL if parsing failed +//Beware, Spell CAN be null when the function is called by the AI trying to analyze the effects of a given card +MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated, int forceUEOT){ + size_t found; + + string whitespaces (" \t\f\v\n\r"); + + found=s.find_last_not_of(whitespaces); + if (found!=string::npos) + s.erase(found+1); + else return NULL; + + found=s.find_first_not_of(whitespaces); + if (found!=string::npos) + s.erase(0,found); + else return NULL; + + //TODO This block redundant with calling function + if (!card && spell) card = spell->source; + if (!card) return NULL; + MTGCardInstance * target = card->target; + if (!target) target = card; + + TriggeredAbility * trigger = NULL; + trigger = parseTrigger(s,id,spell,card,target); + //Dirty way to remove the trigger text (could get in the way) + if (trigger){ + found = s.find(":"); + string s1 = s.substr(found+1); + MTGAbility * a = parseMagicLine(s1, id, spell, card,activated); + if (!a){ + delete trigger; + return NULL; + } + return NEW GenericTriggeredAbility(id,card,trigger,a,NULL,target); + } + + int doTap = 0; //Tap in the cost ? + if (s.find("{t}") != string::npos) doTap = 1; + + size_t delimiter = s.find("}:"); + size_t firstNonSpace = s.find_first_not_of(" "); + if (delimiter!= string::npos && firstNonSpace !=string::npos && s[firstNonSpace] == '{'){ + ManaCost * cost = ManaCost::parseManaCost(s.substr(0,delimiter+1),NULL,card); + if (doTap || (cost && !cost->isNull())){ + string s1 = s.substr(delimiter+2); + + MTGAbility * a = parseMagicLine(s1, id, spell, card, 1); + if (!a){ + OutputDebugString("ABILITYFACTORY Error parsing:"); + OutputDebugString(s.c_str()); + OutputDebugString("\n"); + return NULL; + } + + //A stupid Special case for ManaProducers because they don't use the stack :( + AManaProducer * amp = dynamic_cast(a); + if (amp){ + amp->cost = cost; + amp->oneShot = 0; + amp->tap = doTap; + return amp; + } + + int limit = 0; + unsigned int limit_str = s.find("limit:"); + if (limit_str != string::npos){ + limit = atoi(s.substr(limit_str+6).c_str()); + } + + TargetChooser * tc = NULL; + //Target Abilities + found = s.find("target("); + if (found != string::npos){ + int end = s.find(")", found); + string starget = s.substr(found + 7,end - found - 7); + TargetChooserFactory tcf; + tc = tcf.createTargetChooser(starget, card); + } + + if (tc) return NEW GenericTargetAbility(id, card, tc, a,cost, doTap,limit); + return NEW GenericActivatedAbility(id, card, a,cost,doTap,limit); + } + SAFE_DELETE(cost); + } + + //kicker cost + found = s.find("kicker "); + if (found == 0){ + if (spell && spell->kickerWasPaid()){ + string s1 = s.substr(found+7); + return parseMagicLine(s1,id,spell, card); + } + return NULL; + } + //When...comes into play, you may... + found = s.find("may "); + if (found == 0){ + string s1 = s.substr(found+4); + MTGAbility * a1 = parseMagicLine(s1,id,spell, card); + if (!a1) return NULL; + TargetChooser * tc = NULL; + //Target Abilities + found = s.find("target("); + if (found != string::npos){ + int end = s.find(")", found); + string starget = s.substr(found + 7,end - found - 7); + TargetChooserFactory tcf; + tc = tcf.createTargetChooser(starget, card); + } + if (tc) a1 = NEW GenericTargetAbility(id, card, tc, a1); + else a1 = NEW GenericActivatedAbility(id, card, a1,NULL); + return NEW MayAbility(id,a1,card); + } + + + //Multiple abilities for ONE cost + found = s.find("&&"); + if (found != string::npos){ + string s1 = s.substr(0,found); + string s2 = s.substr(found+2); + MultiAbility * multi = NEW MultiAbility(id, card,target,NULL,NULL); + MTGAbility * a1 = parseMagicLine(s1,id,spell, card,activated); + MTGAbility * a2 = parseMagicLine(s2,id,spell, card,activated); + multi->Add(a1); + multi->Add(a2); + multi->oneShot=1; + return multi; + } + + //Lord, foreach, aslongas + string lords[] = {"lord(","foreach(", "aslongas(", "all("}; + found = string::npos; + int i = -1; + for (int j = 0; j < 4; ++j){ + size_t found2 = s.find(lords[j]); + if (found2!=string::npos && ((found == string::npos) || found2 < found)){ + found = found2; + i = j; + } + } + if (found != string::npos){ + size_t header = lords[i].size(); + size_t end = s.find(")", found+header); + string s1; + if (found == 0 || end != s.size()-1){ + s1 = s.substr(end+1); + }else{ + s1 = s.substr(0, found); + } + if (end != string::npos){ + int lordIncludeSelf = 1; + size_t other = s1.find("other"); + if ( other != string::npos){ + lordIncludeSelf = 0; + s1.replace(other, 5,""); + } + string lordTargetsString = s.substr(found+header,end-found-header); + TargetChooserFactory tcf; + TargetChooser * lordTargets = tcf.createTargetChooser(lordTargetsString, card); + + if (!lordTargets){ + OutputDebugString("MTGABILITY: Parsing Error:"); + OutputDebugString(s.c_str()); + OutputDebugString("\n"); + return NULL; + } + + MTGAbility * a = parseMagicLine(s1,id,spell, card,0,activated); //activated lords usually force an end of turn ability + if (!a){ + SAFE_DELETE(lordTargets); + return NULL; + } + MTGAbility * result = NULL; + int oneShot = 0; + if (activated) oneShot = 1; + if (card->hasType("sorcery") || card->hasType("instant")) oneShot = 1; + if (i == 3) oneShot = 1; + if (a->oneShot) oneShot = 1; + Damageable * _target = NULL; + if (spell) _target = spell->getNextDamageableTarget(); + if (!_target) _target = target; + switch(i){ + case 0: result = NEW ALord(id, card, lordTargets, lordIncludeSelf, a); break; + case 1: result = NEW AForeach(id, card, _target,lordTargets, lordIncludeSelf, a); break; + case 2: result = NEW AAsLongAs(id, card, _target,lordTargets, lordIncludeSelf, a); break; + case 3: result = NEW ALord(id, card, lordTargets, lordIncludeSelf, a); break; + default: result = NULL; + } + if (result) result->oneShot = oneShot; + return result; + } + return NULL; + } + + + //Fizzle (counterspell...) + found = s.find("fizzle"); + if (found != string::npos){ + Spell * starget = NULL; + if (spell) starget = spell->getNextSpellTarget(); + MTGAbility * a = NEW AAFizzler(id,card,starget); + a->oneShot = 1; + return a; + } + + //Regeneration + found = s.find("regenerate"); + if (found != string::npos){ + MTGAbility * a = NEW AStandardRegenerate(id,card,target); + a->oneShot = 1; + return a; + } + + + //Token creator. Name, type, p/t, abilities + found = s.find("token("); + if (found != string::npos){ + int end = s.find(",", found); + string sname = s.substr(found + 6,end - found - 6); + int previous = end+1; + end = s.find(",",previous); + string stypes = s.substr(previous,end - previous); + previous = end+1; + end = s.find(",",previous); + string spt = s.substr(previous,end - previous); + int power, toughness; + parsePowerToughness(spt,&power, &toughness); + string sabilities = s.substr(end+1); + WParsedInt * multiplier = NULL; + found = s.find("*"); + if (found != string::npos)multiplier = NEW WParsedInt(s.substr(found+1),spell,card); + ATokenCreator * tok = NEW ATokenCreator(id,card,NULL,sname,stypes,power,toughness,sabilities,0, multiplier); + tok->oneShot = 1; + return tok; + } + + + //MoveTo Move a card from a zone to another + found = s.find("moveto("); + if (found != string::npos){ + int end = s.find(")",found+1); + string szone = s.substr(found + 7,end - found - 7); + + //hack for http://code.google.com/p/wagic/issues/detail?id=120 + //We assume that auras don't move their own target... + if (card->hasType("aura")) target = card; + + MTGAbility * a = NEW AAMover(id,card,target,szone); + a->oneShot = 1; + return a; + + } + + //Copy a target + found = s.find("copy "); + if (found != string::npos){ + MTGAbility * a = NEW AACopier(id,card,target); + a->oneShot = 1; + return a; + } + + + //Bury, destroy + string destroys[] = {"bury","destroy"}; + int destroyTypes[]= {1, 0}; + for (int i = 0; i < 2; ++i){ + found = s.find(destroys[i]); + if (found != string::npos){ + int bury = destroyTypes[i]; + MTGAbility * a = NEW AADestroyer(id,card,target,bury); + a->oneShot = 1; + return a; + } + } + + + int who = TargetChooser::UNSET; + if (s.find(" controller") != string::npos) who=TargetChooser::CONTROLLER; + if (s.find(" opponent") != string::npos) who=TargetChooser::OPPONENT; + if (s.find(" targetcontroller") != string::npos) who=TargetChooser::TARGET_CONTROLLER; + + found = s.find("ueot"); + if (found!= string::npos) forceUEOT = 1; + + //PreventCombat Damage + found = s.find("preventallcombatdamage"); + if (found != string::npos){ + + + string to = ""; + string from = ""; + + found = s.find("to("); + if (found != string::npos){ + size_t end = s.find (")", found); + to = s.substr(found+3,end - found - 3); + } + + found = s.find("from("); + if (found != string::npos){ + size_t end = s.find (")", found); + from = s.substr(found+5,end - found - 5); + } + + MTGAbility * ab; + if (forceUEOT){ + ab = NEW APreventAllCombatDamageUEOT(id,card,to,from); + }else{ + ab = NEW APreventAllCombatDamage(id,card,to,from); + } + return ab; + } + + + //Damage + found = s.find("damage"); + if (found != string::npos){ + unsigned int start = s.find(":",found); + if (start == string::npos) start = s.find(" ",found); + unsigned int end = s.find(" ",start); + int damage; + if (end != string::npos){ + damage = atoi(s.substr(start+1,end-start-1).c_str()); + }else{ + damage = atoi(s.substr(start+1).c_str()); + } + + Damageable * d = NULL; + if (spell) d = spell->getNextDamageableTarget(); + MTGAbility * a = NEW AADamager(id,card,d, damage, NULL, 0, who); + a->oneShot = 1; + return a; + } + + + //gain/lose life + found = s.find("life:"); + if (found != string::npos){ + unsigned int start = found+4; + unsigned int end = s.find(" ",start); + int life; + if (end != string::npos){ + life = atoi(s.substr(start+1,end-start-1).c_str()); + }else{ + life = atoi(s.substr(start+1).c_str()); + } + + Damageable * d = NULL; + if (spell) d = spell->getNextPlayerTarget(); + MTGAbility * a = NEW AALifer(id,card,d,life,NULL,0,who); + a->oneShot = 1; + return a; + } + + //Draw + found = s.find("draw:"); + if (found != string::npos){ + unsigned int start = s.find(":",found); + unsigned int end = s.find(" ",start); + int nbcards; + if (end != string::npos){ + nbcards = atoi(s.substr(start+1,end-start-1).c_str()); + }else{ + nbcards = atoi(s.substr(start+1).c_str()); + } + + Targetable * t = NULL; + if (spell) t = spell->getNextPlayerTarget(); + MTGAbility * a = NEW AADrawer(id,card,t,NULL,nbcards,0,who); + a->oneShot = 1; + return a; + } + + //Deplete + found = s.find("deplete:"); + if (found != string::npos){ + unsigned int start = s.find(":",found); + unsigned int end = s.find(" ",start); + int nbcards; + if (end != string::npos){ + nbcards = atoi(s.substr(start+1,end-start-1).c_str()); + }else{ + nbcards = atoi(s.substr(start+1).c_str()); + } + + Targetable * t = NULL; + if (spell) t = spell->getNextPlayerTarget(); + MTGAbility * a = NEW AADepleter(id,card,t,nbcards,NULL,0,who); + a->oneShot = 1; + return a; + } + + //Shuffle + found = s.find("shuffle"); + if (found != string::npos){ + Targetable * t = NULL; + if (spell) t = spell->getNextPlayerTarget(); + MTGAbility * a = NEW AAShuffle(id,card,t,NULL,0,who); + a->oneShot = 1; + return a; + } + + /* + //CannotBeBlockedBy + found = s.find("cantbeblockedby("); + if (found != string::npos){ + int end = s.find(")",found); + string starget = s.substr(16, end - 16); + TargetChooserFactory tcf; + tc = tcf.createTargetChooser(starget,card); + return NULL; //NEW ACantBlock(tc); //hu ? CantBlock(tc); + } + +*/ + //Discard + found = s.find("discard:"); + if (found != string::npos){ + unsigned int start = s.find(":",found); + unsigned int end = s.find(" ",start); + int nbcards; + if (end != string::npos){ + nbcards = atoi(s.substr(start+1,end-start-1).c_str()); + }else{ + nbcards = atoi(s.substr(start+1).c_str()); + } + + Targetable * t = NULL; + if (spell) t = spell->getNextPlayerTarget(); + MTGAbility * a = NEW AARandomDiscarder (id, card, t,nbcards,NULL,0,who); + a->oneShot = 1; + return a; + } + + + + //rampage + found = s.find("rampage("); + if (found != string::npos){ + int end = s.find(",", found); + string spt = s.substr(8,end - 1); + int power, toughness; + if (parsePowerToughness(spt,&power, &toughness)){ + int MaxOpponent = atoi(s.substr(end+1,end+2).c_str()); + return NEW ARampageAbility(id,card,power,toughness,MaxOpponent); + } + return NULL; + } + + + //counter + found = s.find("counter("); + if (found != string::npos){ + found+=8; + int nb = 1; + size_t end = s.find(")", found); + size_t separator = s.find(",", found); + if (separator != string::npos){ + string nbstr = s.substr(separator+1,end-separator-1); + nb = atoi(nbstr.c_str()); + end = separator; + } + string spt = s.substr(found,end-found); + int power, toughness; + if ( parsePowerToughness(spt,&power, &toughness)){ + MTGAbility * a = NEW AACounter(id,card,target,power,toughness,nb); + a->oneShot = 1; + return a; + } + return NULL; + } + + + + + //Becomes... (animate artifact...: becomes(Creature, manacost/manacost) + found = s.find("becomes("); + if (found != string::npos){ + size_t real_end = s.find(")", found); + size_t end = s.find(",", found); + if (end == string::npos) end = real_end; + string stypes = s.substr(found + 8,end - found - 8); + WParsedPT * pt = NULL; + string sabilities; + if (end != real_end){ + int previous = end+1; + end = s.find(",",previous); + if (end == string::npos) end = real_end; + string temp = s.substr(previous, end - previous); + pt = NEW WParsedPT(temp,spell,card); + if (!pt->ok){ + SAFE_DELETE(pt); + sabilities = temp; + } + } + if (pt && end != real_end){ + sabilities = s.substr(end+1, real_end - end); + } + MTGAbility * ab; + if (forceUEOT){ + ab = NEW ABecomesUEOT(id,card,target,stypes,pt,sabilities); + }else{ + ab = NEW ABecomes(id,card,target,stypes,pt,sabilities); + } + return ab; + } + + //Change Power/Toughness + WParsedPT * wppt = NEW WParsedPT(s,spell,card); + if (wppt->ok){ + if (!activated){ + if(card->hasType("instant") || card->hasType("sorcery") || forceUEOT){ + return NEW AInstantPowerToughnessModifierUntilEOT(id, card, target,wppt); + } + return NEW APowerToughnessModifier(id, card, target,wppt); + } + return NEW APowerToughnessModifierUntilEndOfTurn(id,card,target,wppt); + }else{ + delete wppt; + } + + + + //Mana Producer + found = s.find("add"); + if (found != string::npos){ + ManaCost * output = ManaCost::parseManaCost(s.substr(found)); + Targetable * t = NULL; + if (spell) t = spell->getNextPlayerTarget(); + MTGAbility * a = NEW AManaProducer(id, card, t, output, NULL, 1, who); + a->oneShot = 1; + return a; + } + + + //Gain/loose Ability + for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++){ + found = s.find(Constants::MTGBasicAbilities[j]); + if (found == 0 || found == 1){ + int modifier = 1; + if (found > 0 && s[found-1] == '-') modifier = 0; + if (!activated){ + if(card->hasType("instant") || card->hasType("sorcery") || forceUEOT){ + return NEW AInstantBasicAbilityModifierUntilEOT(id, card,target, j,modifier); + } + return NEW ABasicAbilityModifier(id, card,target, j,modifier); + } + return NEW ABasicAbilityAuraModifierUntilEOT(id, card,target, NULL,j,modifier); + } + } + + //Untapper (Ley Druid...) + found = s.find("untap"); + if (found != string::npos){ + MTGAbility * a = NEW AAUntapper(id,card,target); + a->oneShot = 1; + return a; + } + + //Tapper (icy manipulator) + found = s.find("tap"); + if (found != string::npos){ + MTGAbility * a = NEW AATapper(id,card,target); + a->oneShot = 1; + return a; + } + + return NULL; + +} + +//Tells the AI if the ability should target itself or an ennemy +int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, TargetChooser * tc){ + if (!a) return BAKA_EFFECT_DONTKNOW; + + if (GenericTargetAbility * abi = dynamic_cast(a)) { + if (mode == MODE_PUTINTOPLAY) return BAKA_EFFECT_GOOD; + return abilityEfficiency(abi->ability,p, mode, abi->tc); + } + if (GenericActivatedAbility * abi = dynamic_cast(a)) { + if (mode == MODE_PUTINTOPLAY) return BAKA_EFFECT_GOOD; + return abilityEfficiency(abi->ability,p, mode,tc); + } + if (MultiAbility * abi = dynamic_cast(a)) return abilityEfficiency(abi->abilities[0],p, mode,tc ); + if (MayAbility * abi = dynamic_cast(a)) return abilityEfficiency(abi->ability,p, mode,tc); + if (ALord * abi = dynamic_cast(a)) { + int myCards = countCards(abi->tc, p); + int theirCards = countCards(abi->tc, p->opponent()); + int efficiency = abilityEfficiency(abi->ability,p, mode,tc); + if (myCards > theirCards) return efficiency; + return -efficiency; + } + if (AAsLongAs * abi = dynamic_cast(a)) return abilityEfficiency(abi->ability,p, mode,tc); + if (AForeach * abi = dynamic_cast(a)) return abilityEfficiency(abi->ability,p, mode,tc); + if (dynamic_cast(a)) return BAKA_EFFECT_BAD; + if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; + if (dynamic_cast(a)) return BAKA_EFFECT_BAD; + if (AACounter * ac = dynamic_cast(a)) { + bool negative_effect = ac->power < 0 || ac->toughness < 0; + if ((ac->nb > 0 && negative_effect) || (ac->nb < 0 && !negative_effect)) return BAKA_EFFECT_BAD; + return BAKA_EFFECT_GOOD ; + } + if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; + + if (AAMover * aam = dynamic_cast(a)) { + MTGGameZone * z = aam->destinationZone(); + if (tc && tc->targetsZone(p->game->library)){ + if (z == p->game->hand || z == p->game->inPlay) return BAKA_EFFECT_GOOD; + } + return BAKA_EFFECT_BAD; //TODO + } + + if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; + if (dynamic_cast(a)) return BAKA_EFFECT_BAD; + if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; + if (dynamic_cast(a)) return BAKA_EFFECT_BAD; + if (AALifer * abi = dynamic_cast(a)) return abi->life > 0 ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD; + if (dynamic_cast(a)) return BAKA_EFFECT_BAD; + if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; + if (dynamic_cast(a)) return BAKA_EFFECT_BAD; + if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; + if (AInstantPowerToughnessModifierUntilEOT * abi = dynamic_cast(a)) return (abi->wppt->power.getValue()>=0 && abi->wppt->toughness.getValue()>=0) ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD; + if (APowerToughnessModifier * abi = dynamic_cast(a)) return (abi->wppt->power.getValue()>=0 && abi->wppt->toughness.getValue()>=0) ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD; + if (APowerToughnessModifierUntilEndOfTurn * abi = dynamic_cast(a)) return abilityEfficiency(abi->ability, p, mode,tc); + + map badAbilities; + badAbilities[Constants::CANTATTACK] = true; + badAbilities[Constants::CANTBLOCK] = true; + badAbilities[Constants::CLOUD] = true; + badAbilities[Constants::DEFENDER] = true; + badAbilities[Constants::DOESNOTUNTAP] = true; + badAbilities[Constants::MUSTATTACK] = true; + + if (AInstantBasicAbilityModifierUntilEOT * abi = dynamic_cast(a)) { + int result = badAbilities[abi->ability] ? BAKA_EFFECT_BAD : BAKA_EFFECT_GOOD; + return (abi->value > 0) ? result : -result; + } + if (ABasicAbilityModifier * abi = dynamic_cast(a)){ + int result = (badAbilities[abi->ability]) ? BAKA_EFFECT_BAD : BAKA_EFFECT_GOOD; + return (abi->modifier > 0) ? result : -result; + } + if (ABasicAbilityAuraModifierUntilEOT * abi = dynamic_cast(a)) + return abilityEfficiency(abi->ability, p, mode); + if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; + return BAKA_EFFECT_DONTKNOW; +} + +//Returns the "X" cost that was paid for a spell +int AbilityFactory::computeX(Spell * spell, MTGCardInstance * card){ + if (spell) return spell->computeX(card); + return 0; +} + +//Some basic functionalities that can be added automatically in the text file +/* + * Several objects are computed from the text string, and have a direct influence on what action we should take + * (direct impact on the game such as draw a card immediately, or create a New GameObserver and add it to the Abilities,etc..) + * These objects are: + * - trigger (if there is an "@" in the string, this is a triggered ability) + * - target (if there ie a "target(" in the string, then this is a TargetAbility) + * - doTap (a dirty way to know if tapping is included in the cost... + */ +int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int mode, TargetChooser * tc){ + int dryMode = 0; + if (!spell) dryMode = 1; + + if (!card && spell) card = spell->source; + if (!card) return 0; + MTGCardInstance * target = card->target; + if (!target) target = card; + string magicText = card->magicText; + if (card->alias && magicText.size() == 0){ + //An awful way to get access to the aliasedcard + MTGCard * c = GameObserver::GetInstance()->players[0]->game->collection->getCardById(card->alias); + if (!c) return 0; + magicText = c->magicText; + } + string line; + int size = magicText.size(); + if (size == 0) return 0; + unsigned int found; + int result = id; + + + while (magicText.size()){ + found = magicText.find("\n"); + if (found != string::npos){ + line = magicText.substr(0,found); + magicText = magicText.substr(found+1); + }else{ + line = magicText; + magicText = ""; + } + + MTGAbility * a = parseMagicLine(line, result, spell, card); + if (dryMode){ + result = abilityEfficiency(a, card->controller(),mode,tc); + SAFE_DELETE(a); + return result; + } + + if (a){ + if (a->oneShot){ + a->resolve(); + delete(a); + }else{ + a->addToGame(); + } + result++; + }else{ + OutputDebugString("ABILITYFACTORY ERROR: Parser returned NULL\n"); + } + } + return result; + +} + +void AbilityFactory::addAbilities(int _id, Spell * spell){ + MTGCardInstance * card = spell->source; + + + if (spell->getNbTargets()==1){ + card->target = spell->getNextCardTarget(); + if (card->target && !spell->tc->canTarget(card->target)){ + MTGPlayerCards * zones = card->controller()->game; + zones->putInZone(card,spell->from,card->owner->game->graveyard); + return; //fizzle + } + } + _id = magicText(_id, spell); + + GameObserver * game = GameObserver::GetInstance(); + MTGPlayerCards * zones = card->controller()->game; + + + int id = card->getId(); + if (card->alias) id = card->alias; + switch (id){ + case 1092: //Aladdin's lamp + { + AAladdinsLamp * ability = NEW AAladdinsLamp(_id, card); + game->addObserver(ability); + break; + } + case 1095: //Armageddon clock + { + AArmageddonClock * ability = NEW AArmageddonClock(_id,card); + game->addObserver(ability); + break; + } + case 1097: //Black Vise + { + game->addObserver( NEW ALifeZoneLink(_id ,card, Constants::MTG_PHASE_UPKEEP, 4)); + break; + } + case 1191: //Blue Elemental Blast + { + if (card->target){ + card->target->controller()->game->putInGraveyard(card->target); + }else{ + Spell * starget = spell->getNextSpellTarget(); + game->mLayers->stackLayer()->Fizzle(starget); + } + break; + } + case 1237: //Channel + { + game->addObserver(NEW AChannel(_id, card)); + break; + } + case 1282: //Chaoslace + { + if (card->target){ + card->target->setColor(Constants::MTG_COLOR_RED, 1); + }else{ + Spell * starget = spell->getNextSpellTarget(); + starget->source->setColor(Constants::MTG_COLOR_RED, 1); + } + break; + } + case 1335: //Circle of protection : black + { + game->addObserver(NEW ACircleOfProtection( _id,card, Constants::MTG_COLOR_BLACK)); + break; + } + case 1336: //Circle of protection : blue + { + game->addObserver(NEW ACircleOfProtection( _id,card, Constants::MTG_COLOR_BLUE)); + break; + } + case 1337: //Circle of protection : green + { + game->addObserver(NEW ACircleOfProtection( _id,card, Constants::MTG_COLOR_GREEN)); + break; + } + case 1338: //Circle of protection : red + { + game->addObserver(NEW ACircleOfProtection( _id,card, Constants::MTG_COLOR_RED)); + break; + } + case 1339: //Circle of protection : white + { + game->addObserver(NEW ACircleOfProtection( _id,card, Constants::MTG_COLOR_WHITE)); + break; + } + case 1101: //clockwork Beast + { + game->addObserver(NEW AClockworkBeast(_id,card)); + break; + } + case 1102: //Conservator + { + game->addObserver(NEW AConservator(_id,card)); + break; + } + + case 1197: //Creature Bond + { + game->addObserver(NEW ACreatureBond(_id,card, card->target)); + break; + } + case 1103: //Crystal Rod + { + int cost[] = {Constants::MTG_COLOR_BLUE, 1}; + ASpellCastLife* ability = NEW ASpellCastLife(_id, card, Constants::MTG_COLOR_WHITE,NEW ManaCost(cost,1) , 1); + game->addObserver(ability); + break; + } + case 1152: //Deathlace + { + if (card->target){ + card->target->setColor(Constants::MTG_COLOR_BLACK, 1); + }else{ + Spell * starget = spell->getNextSpellTarget(); + starget->source->setColor(Constants::MTG_COLOR_BLACK, 1); + } + break; + } + case 1106: //Disrupting Scepter + { + ADisruptingScepter * ability = NEW ADisruptingScepter(_id,card); + game->addObserver(ability); + break; + } + case 1284: //Dragon Whelp + { + game->addObserver(NEW ADragonWhelp(_id,card)); + break; + } + + case 1345: //Farmstead + { + game->addObserver(NEW AFarmstead(_id, card,card->target)); + break; + } + case 1291: //Fireball + { + int x = computeX(spell,card); + game->addObserver(NEW AFireball(_id, card,spell, x)); + break; + } + case 1245: //Force of Nature + { + game->addObserver(NEW AForceOfNature(_id,card)); + break; + } + case 1110: //Glasses Of Urza + { + AGlassesOfUrza * ability = NEW AGlassesOfUrza(_id,card); + game->addObserver(ability); + break; + } + case 1112: //Howling Mine + { + game->addObserver(NEW AHowlingMine(_id, card)); + break; + } + case 1252: //Instill Energy + { + game->addObserver(NEW AUntaperOnceDuringTurn(_id, card, card->target, NEW ManaCost())); + break; + } + case 1113: //Iron Star + { + int cost[] = {Constants::MTG_COLOR_ARTIFACT, 1}; + ASpellCastLife* ability = NEW ASpellCastLife(_id, card, Constants::MTG_COLOR_RED,NEW ManaCost(cost,1) , 1); + game->addObserver(ability); + break; + } + case 1351: // Island Sanctuary + { + game->addObserver(NEW AIslandSanctuary(_id, card)); + break; + } + case 1114: //Ivory cup + { + int cost[] = {Constants::MTG_COLOR_ARTIFACT, 1}; + ASpellCastLife* ability = NEW ASpellCastLife(_id, card, Constants::MTG_COLOR_WHITE,NEW ManaCost(cost,1) , 1); + game->addObserver(ability); + break; + } + case 1115: //Ivory Tower + { + game->addObserver(NEW ALifeZoneLink(_id ,card, Constants::MTG_PHASE_UPKEEP, 4, 1, 1)); + break; + } + case 1117: //Jandors Ring + { + game->addObserver(NEW AJandorsRing( _id, card)); + break; + } + case 1121: //Kormus Bell + { + game->addObserver(NEW AConvertLandToCreatures(id, card, "swamp")); + break; + } + case 1254: //Kudzu + { + game->addObserver(NEW AKudzu(id, card, card->target)); + break; + } + case 1257: //Lifelace + { + if (card->target){ + card->target->setColor(Constants::MTG_COLOR_GREEN, 1); + }else{ + Spell * starget = spell->getNextSpellTarget(); + starget->source->setColor(Constants::MTG_COLOR_GREEN, 1); + } + break; + } + + case 1259: //Living lands + { + game->addObserver(NEW AConvertLandToCreatures(id, card, "forest")); + break; + } + case 1124: //Mana Vault + { + int output[] = {Constants::MTG_COLOR_ARTIFACT, 3}; + game->addObserver(NEW AManaProducer(_id,card,card,NEW ManaCost(output,1))); + int cost[] = {Constants::MTG_COLOR_ARTIFACT, 4}; + game->addObserver(NEW AUntapManaBlocker(_id+1, card, NEW ManaCost(cost,1))); + game->addObserver(NEW ARegularLifeModifierAura(_id+2, card, card, Constants::MTG_PHASE_DRAW, -1, 1)); + break; + } + case 1215: //Power Leak + { + game->addObserver( NEW APowerLeak(_id ,card, card->target)); + break; + } + case 1358: //Purelace + { + if (card->target){ + card->target->setColor(Constants::MTG_COLOR_WHITE, 1); + }else{ + Spell * starget = spell->getNextSpellTarget(); + starget->source->setColor(Constants::MTG_COLOR_WHITE, 1); + } + break; + } + case 1312: //Red Elemental Blast + { + if (card->target){ + card->target->controller()->game->putInGraveyard(card->target); + }else{ + Spell * starget = spell->getNextSpellTarget(); + game->mLayers->stackLayer()->Fizzle(starget); + } + break; + } + case 1136: //Soul Net + { + game->addObserver( NEW ASoulNet(_id ,card)); + break; + } + case 1139: //The Rack + { + game->addObserver( NEW ALifeZoneLink(_id ,card, Constants::MTG_PHASE_UPKEEP, -3)); + break; + } + case 1140: //Throne of Bone + { + int cost[] = {Constants::MTG_COLOR_ARTIFACT, 1}; + ASpellCastLife* ability = NEW ASpellCastLife(_id, card, Constants::MTG_COLOR_BLACK,NEW ManaCost(cost,1) , 1); + game->addObserver(ability); + break; + } + + case 1142: //Wooden Sphere + { + int cost[] = {Constants::MTG_COLOR_ARTIFACT, 1}; + ASpellCastLife* ability = NEW ASpellCastLife(_id, card, Constants::MTG_COLOR_GREEN,NEW ManaCost(cost,1) , 1); + game->addObserver(ability); + break; + } + case 1143: //Animate Dead + { + AAnimateDead * a = NEW AAnimateDead(_id, card, card->target); + game->addObserver(a); + card->target = ((MTGCardInstance * )a->target); + break; + } + case 1156: //Drain Life + { + Damageable * target = spell->getNextDamageableTarget(); + int x = spell->cost->getConvertedCost() - 2; //TODO Fix that !!! + X should be only black mana, that needs to be checked ! + game->mLayers->stackLayer()->addDamage(card, target, x); + if (target->life < x) x = target->life; + game->currentlyActing()->life+=x; + break; + } + case 1159: //Erg Raiders + { + AErgRaiders* ability = NEW AErgRaiders(_id, card); + game->addObserver(ability); + break; + } + case 1202: //Hurkyl's Recall + { + Player * player = spell->getNextPlayerTarget(); + if (player){ + for (int i = 0; i < 2; i++){ + MTGInPlay * inplay = game->players[i]->game->inPlay; + for (int j= inplay->nb_cards -1 ; j >=0 ; j--){ + MTGCardInstance * card = inplay->cards[j]; + if (card->owner == player && card->hasType("artifact")){ + player->game->putInZone(card, inplay, player->game->hand); + } + } + } + } + break; + } + case 1165: //Hypnotic Specter + { + game->addObserver(NEW AHypnoticSpecter( _id, card)); + break; + } + case 1258: //Living Artifact + { + game->addObserver(NEW ALivingArtifact( _id, card, card->target)); + break; + } + case 1166: //Lord Of The Pit + { + game->addObserver(NEW ALordOfThePit( _id, card)); + break; + } + case 1209: //Mana Short + { + Player * player = spell->getNextPlayerTarget(); + if (player){ + MTGInPlay * inplay = player->game->inPlay; + for (int i = 0; i < inplay->nb_cards; i++){ + MTGCardInstance * current = inplay->cards[i]; + if (current->hasType(Subtypes::TYPE_LAND)) current->tap(); + } + player->getManaPool()->init(); + } + break; + } + case 1167: //Mind Twist + { + int xCost = computeX(spell,card); + for (int i = 0; i < xCost; i++){ + game->opponent()->game->discardRandom(game->opponent()->game->hand); + } + break; + } + case 1171: //Paralyze + { + int cost[] = {Constants::MTG_COLOR_ARTIFACT, 4}; + game->addObserver(NEW AUntapManaBlocker(_id, card,card->target, NEW ManaCost(cost,1))); + card->target->tap(); + break; + } + case 1172: //Pestilence + { + game->addObserver(NEW APestilence(_id, card)); + break; + } + + case 1176: //Sacrifice + { + ASacrifice * ability = NEW ASacrifice(_id, card, card->target); + game->addObserver(ability); + break; + } + case 1224: //Spell Blast + { + int x = computeX(spell,card); + Spell * starget = spell->getNextSpellTarget(); + if (starget){ + if (starget->cost->getConvertedCost() <= x) game->mLayers->stackLayer()->Fizzle(starget); + } + break; + } + case 1192: //BrainGeyser + { + Player * player = spell->getNextPlayerTarget(); + int x = spell->cost->getConvertedCost() - 2; + for (int i = 0; i < x ; i++){ + player->game->drawFromLibrary(); + } + break; + } + case 1194: //Control Magic + { + game->addObserver(NEW AControlStealAura(_id, card, card->target)); + break; + } + case 1218: //Psychic Venom + { + game->addObserver(NEW APsychicVenom(_id, card, card->target)); + break; + } + case 1226: //Steal Artifact + { + game->addObserver( NEW AControlStealAura(_id, card, card->target)); + break; + } + case 1235: //Aspect of Wolf + { + game->addObserver(NEW AAspectOfWolf(_id, card, card->target)); + break; + } + case 1240: //Crumble + { + card->target->controller()->life+= card->target->getManaCost()->getConvertedCost(); + break; + } + case 1251: //Hurricane + { + int x = spell->cost->getConvertedCost() - 1; + for (int i = 0; i < 2 ; i++){ + game->mLayers->stackLayer()->addDamage(card, game->players[i], x); + for (int j = 0; j < game->players[i]->game->inPlay->nb_cards; j++){ + MTGCardInstance * current = game->players[i]->game->inPlay->cards[j]; + if (current->basicAbilities[Constants::FLYING] && current->isCreature()){ + game->mLayers->stackLayer()->addDamage(card, current, x); + } + } + } + break; + } + case 1262: //Regeneration + { + int cost[] = {Constants::MTG_COLOR_GREEN, 1}; + game->addObserver(NEW AStandardRegenerate(_id,card,card->target,NEW ManaCost(cost,1))); + break; + } + + case 1266: //stream of life + { + int x = computeX(spell,card); + spell->getNextPlayerTarget()->life += x; + break; + } + + + case 1231: //Volcanic Eruption + { + int x = computeX(spell,card); + int _x = x; + MTGCardInstance * target = spell->getNextCardTarget(); + while(target && _x){ + target->destroy(); + _x--; + target = spell->getNextCardTarget(target); + } + x-=_x; + for (int i = 0; i < 2 ; i++){ + game->mLayers->stackLayer()->addDamage(card, game->players[i], x); + for (int j = 0; j < game->players[i]->game->inPlay->nb_cards; j++){ + MTGCardInstance * current = game->players[i]->game->inPlay->cards[j]; + if (current->isCreature()){ + game->mLayers->stackLayer()->addDamage(card, current, x); + } + } + } + break; + } + + case 1285: //Dwarven Warriors + { + CreatureTargetChooser * tc = NEW CreatureTargetChooser(card); + tc->maxpower = 2; + game->addObserver(NEW ABasicAbilityModifierUntilEOT(_id, card, Constants::UNBLOCKABLE, NULL,tc)); + break; + } + case 1288: //EarthBind + { + game->addObserver(NEW AEarthbind(_id, card, card->target)); + break; + } + case 1289: //earthquake + { + int x = computeX(spell,card); + for (int i = 0; i < 2 ; i++){ + game->mLayers->stackLayer()->addDamage(card, game->players[i], x); + for (int j = 0; j < game->players[i]->game->inPlay->nb_cards; j++){ + MTGCardInstance * current = game->players[i]->game->inPlay->cards[j]; + if (!current->basicAbilities[Constants::FLYING] && current->isCreature()){ + game->mLayers->stackLayer()->addDamage(card, current, x); + } + } + } + break; + } + case 1344: //Eye for an Eye + { + Damage * damage = spell->getNextDamageTarget(); + if (damage){ + game->mLayers->stackLayer()->addDamage(card,damage->source->controller(),damage->damage); + } + break; + } + case 1243: //Fastbond + { + game->addObserver(NEW AFastbond(_id, card)); + break; + } + case 1238: //Cockatrice + { + game->addObserver(NEW AOldSchoolDeathtouch(_id,card)); + break; + } + case 1362: //Reverse polarity + { + ActionStack * as = game->mLayers->stackLayer(); + Player * controller = card->controller(); + Damage * current = ((Damage *)as->getNext(NULL,ACTION_SPELL, RESOLVED_OK)); + while(current){ + if (current->target == controller && current->source->hasType("artifact")){ + controller->life+= current->damage * 2; + } + current = ((Damage *)as->getNext(current,ACTION_SPELL, RESOLVED_OK)); + } + break; + } + case 1225: //Stasis + { + game->addObserver(NEW AStasis(_id, card)); + break; + } + + case 1367: //Swords to Plowshares + { + Player * p = card->target->controller(); + p->life+= card->target->power; + p->game->putInZone(card->target,p->game->inPlay,card->owner->game->removedFromGame); + break; + } + case 1267: //Thicket Basilic + { + game->addObserver(NEW AOldSchoolDeathtouch(_id,card)); + break; + } + case 1227: //Toughtlace + { + if (card->target){ + card->target->setColor(Constants::MTG_COLOR_BLUE, 1); + }else{ + Spell * starget = spell->getNextSpellTarget(); + starget->source->setColor(Constants::MTG_COLOR_BLUE, 1); + } + break; + } + + //Addons Legends + case 1427: //Abomination (does not work make the game crash) + { + game->addObserver(NEW AAbomination(_id,card)); + break; + } + case 1533: //Livingplane + { + game->addObserver(NEW AConvertLandToCreatures(id, card, "land")); + break; + } + case 1607: //Divine Offering + { + card->target->controller()->game->putInGraveyard(card->target); + game->currentlyActing()->life+= card->target->getManaCost()->getConvertedCost(); + break; + } + case 1480: //Energy Tap + { + card->target->tap(); + int mana = card->target->getManaCost()->getConvertedCost(); + game->currentlyActing()->getManaPool()->add(Constants::MTG_COLOR_ARTIFACT, mana); + } + + case 1614: // Great Defender + { + int toughness = card->target->getManaCost()->getConvertedCost(); + int power = 0; + game->addObserver(NEW AInstantPowerToughnessModifierUntilEOT(id, card, card->target, NEW WParsedPT(power,toughness))); + } + + case 1703: //Pendelhaven + { + CreatureTargetChooser * tc = NEW CreatureTargetChooser(card); + tc->maxpower = 1; + tc->maxtoughness =1; + game->addObserver(NEW ATargetterPowerToughnessModifierUntilEOT(id, card, NEW WParsedPT(1,2), NEW ManaCost(),tc)); + break; + } + + //Addons ICE-AGE Cards + + case 2660: //Word of Blasting + { + card->target->controller()->life-= card->target->getManaCost()->getConvertedCost(); + break; + } + case 2474: //Minion of Leshrac + { + game->addObserver(NEW AMinionofLeshrac( _id, card)); + break; + } + case 2421: //Shield of the Age + { + game->addObserver(NEW AShieldOfTheAge( _id, card)); + break; + } + case 2435: //Whalebone Glider + { + int cost[] = {Constants::MTG_COLOR_ARTIFACT,2}; + CreatureTargetChooser * tc = NEW CreatureTargetChooser(card); + tc->maxpower = 3; + game->addObserver(NEW ABasicAbilityModifierUntilEOT(_id, card, Constants::FLYING, NEW ManaCost(cost,1),tc)); + break; + } + case 2393: //Aegis of the Meek work but work also for 0/1 creatures... :D + { + int cost[] = {Constants::MTG_COLOR_ARTIFACT,1}; + CreatureTargetChooser * tc = NEW CreatureTargetChooser(card); + tc->maxpower = 1; + tc->maxtoughness =1; + game->addObserver(NEW ATargetterPowerToughnessModifierUntilEOT(id, card, NEW WParsedPT(1,2), NEW ManaCost(cost,1),tc)); + break; + } + +//---addon Alliance--- + + case 3194: // Exile + { + game->currentlyActing()->life+=card->target->toughness; + break; + } + +// --- addon Mirage --- + + case 3410: //Seed of Innocence + { + GameObserver * game = GameObserver::GetInstance(); + for (int i = 0; i < 2 ; i++){ + for (int j = 0; j < game->players[i]->game->inPlay->nb_cards; j++){ + MTGCardInstance * current = game->players[i]->game->inPlay->cards[j]; + if (current->hasType("Artifact")){ + game->players[i]->game->putInGraveyard(current); + current->controller()->life+= current->getManaCost()->getConvertedCost(); + } + } + } + break; + } + +//-- addon 10E--- + + case 129710: //Angelic Chorus + { + game->addObserver( NEW AAngelicChorus(_id,card)); + break; + } + + case 129767: //Threaten + { + game->addObserver( NEW AInstantControlSteal(_id,card,card->target)); + break; + } + case 130373: //Lavaborn Muse + { + game->addObserver( NEW ALavaborn(_id ,card, Constants::MTG_PHASE_UPKEEP, -3,-3)); + break; + } + case 129774: // Traumatize + { + int nbcards; + Player * player = spell->getNextPlayerTarget(); + MTGLibrary * library = player->game->library; + nbcards = (library->nb_cards)/2; + for (int i = 0; i < nbcards; i++){ + if (library->nb_cards) + player->game->putInZone(library->cards[library->nb_cards-1],library, player->game->graveyard); + } + break; + } + + + case 135215: //Sylvan Basilisk + { + game->addObserver( NEW ABasilik (_id ,card)); + break; + } + case 130553:// Beacon of Immortality + { + Player * player = spell->getNextPlayerTarget(); + if (player->life < (INT_MAX / 4) ) player->life += player->life; + zones->putInZone(card,spell->from,zones->library); + zones->library->shuffle(); + break; + } + case 135262:// Beacon of Destruction & unrest + { + zones->putInZone(card,spell->from,zones->library); + zones->library->shuffle(); + break; + } + case 129750: //Sudden Impact + { + Damageable * target = spell->getNextDamageableTarget(); + Player * p = spell->getNextPlayerTarget(); + MTGHand * hand = p->game->hand; + int damage = hand->nb_cards; + game->mLayers->stackLayer()->addDamage(card, target, damage); + break; + } + case 130369: // Soulblast + { + int damage = 0; + Damageable * target = spell->getNextDamageableTarget(); + for (int j = card->controller()->game->inPlay->nb_cards-1; j >=0 ; --j){ + MTGCardInstance * current = card->controller()->game->inPlay->cards[j]; + if (current->hasType(Subtypes::TYPE_CREATURE)){ + card->controller()->game->putInGraveyard(current); + damage+= current->power; + } + } + game->mLayers->stackLayer()->addDamage(card, target, damage); + break; + } + + + case 129698: // Reminisce + { + int nbcards; + Player * player = spell->getNextPlayerTarget(); + MTGLibrary * library = player->game->library; + MTGGraveyard * graveyard = player->game->graveyard; + nbcards = (graveyard->nb_cards); + for (int i = 0; i < nbcards; i++){ + if (graveyard->nb_cards) + player->game->putInZone(graveyard->cards[graveyard->nb_cards-1],graveyard, library); + } + library->shuffle(); + break; + } + + +// --- addon Invasion--- + case 23195: //Artifact Mutation + { + card->target->controller()->game->putInGraveyard(card->target); + int x = card->target->getManaCost()->getConvertedCost(); + ATokenCreator * tok = NEW ATokenCreator(id,card,NEW ManaCost(),"Saproling","creature Saproling",1,1,"green",0); + for (int i=0; i < x; i++){ + tok->resolve(); + } + delete(tok); + break; + } + +// --- addon Ravnica--- + + case 89114: //Psychic Drain + { + Player * player = spell->getNextPlayerTarget(); + MTGLibrary * library = player->game->library; + int x = computeX(spell,card); + for (int i = 0; i < x; i++){ + if (library->nb_cards) + player->game->putInZone(library->cards[library->nb_cards-1],library, player->game->graveyard); + } + game->currentlyActing()->life+= x; + break; + } + + // --- addon ARB--- + case 179614: // Morbid Bloom + { + int x = card->target->toughness; + ATokenCreator * tok = NEW ATokenCreator(id,card,NEW ManaCost(),"Saproling","creature Saproling",1,1,"green",0); + for (int i=0; i < x; i++){ + tok->resolve(); + } + delete(tok); + break; + } + + default: + break; + } + + + + + /* We want to get rid of these basicAbility things. + * basicAbilities themselves are alright, but creating New object depending on them is dangerous + * The main reason is that classes that add an ability to a card do NOT create these objects, and therefore do NOT + * Work. + * For example, setting EXALTED for a creature is not enough right now... + * It shouldn't be necessary to add an object. State based abilities could do the trick + */ + + if (card->basicAbilities[Constants::EXALTED]){ + game->addObserver(NEW AExalted(_id, card)); + } + + // Tested works the first r10 did not function because of the mistake in the array of the definition + if (card->basicAbilities[Constants::FORESTHOME]){ + game->addObserver(NEW AStrongLandLinkCreature(_id, card, "forest")); + } + if (card->basicAbilities[Constants::ISLANDHOME]){ + game->addObserver(NEW AStrongLandLinkCreature(_id, card, "island")); + } + if (card->basicAbilities[Constants::MOUNTAINHOME]){ + game->addObserver(NEW AStrongLandLinkCreature(_id, card,"moutain")); + } + if (card->basicAbilities[Constants::SWAMPHOME]){ + game->addObserver(NEW AStrongLandLinkCreature(_id, card,"swamp")); + } + if (card->basicAbilities[Constants::PLAINSHOME]){ + game->addObserver(NEW AStrongLandLinkCreature(_id, card,"plains")); + } + + if (card->hasType("instant") || card->hasType("sorcery")){ + MTGPlayerCards * zones = card->controller()->game; + zones->putInZone(card,zones->stack,zones->graveyard); + } + + +} + +MTGAbility::MTGAbility(int id, MTGCardInstance * card):ActionElement(id){ + game = GameObserver::GetInstance(); + source = card; + target = card; + aType = MTGAbility::UNKNOWN; + cost = NULL; + forceDestroy = 0; + oneShot = 0; +} + +MTGAbility::MTGAbility(int id, MTGCardInstance * _source,Targetable * _target ):ActionElement(id){ + game = GameObserver::GetInstance(); + source = _source; + target = _target; + aType = MTGAbility::UNKNOWN; + cost = NULL; + forceDestroy = 0; + oneShot = 0; +} + +int MTGAbility::stillInUse(MTGCardInstance * card){ + if (card==source || card==target) return 1; + return 0; +} + +MTGAbility::~MTGAbility(){ + if (!isClone){ + SAFE_DELETE(cost); + } +} + +int MTGAbility::addToGame(){ + GameObserver::GetInstance()->addObserver(this); + return 1; +} + +int MTGAbility::removeFromGame(){ + GameObserver::GetInstance()->removeObserver(this); + return 1; +} + +//returns 1 if this ability needs to be removed from the list of active abilities +int MTGAbility::testDestroy(){ + if (game->mLayers->stackLayer()->has(this)) return 0; + if (waitingForAnswer) return 0; + if (forceDestroy == 1) return 1; + if (forceDestroy == -1) return 0; + if (!game->isInPlay(source) ) return 1; + if (target && !game->isInPlay((MTGCardInstance *)target)) return 1; + return 0; +} + + + +int MTGAbility::fireAbility(){ + game->mLayers->stackLayer()->addAbility(this); + return 1; +} + +ostream& MTGAbility::toString(ostream& out) const +{ + return out << "MTGAbility ::: menuText : " << menuText + << " ; game : " << game + << " ; forceDestroy : " << forceDestroy + << " ; cost : " << cost + << " ; target : " << target + << " ; aType : " << aType + << " ; source : " << source; +} + +// + +ActivatedAbility::ActivatedAbility(int id, MTGCardInstance * card, ManaCost * _cost, int _playerturnonly,int tap):MTGAbility(id,card), playerturnonly(_playerturnonly), needsTapping(tap){ + cost = _cost; +} + + +int ActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ + Player * player = game->currentPlayer; + if (!playerturnonly) player = game->currentlyActing(); + if (card == source && source->controller()==player && player==game->currentlyActing() && (!needsTapping || (!source->isTapped() && !source->hasSummoningSickness()))){ + if (!cost) return 1; + if (!mana) mana = player->getManaPool(); + if (!mana->canAfford(cost)) return 0; + return 1; + } + return 0; +} + +int ActivatedAbility::reactToClick(MTGCardInstance * card){ + if (!isReactingToClick(card)) return 0; + if (cost){ + cost->setExtraCostsAction(this, card); + if (!cost->isExtraPaymentSet()){ + game->waitForExtraPayment = cost->extraCosts; + return 0; + } + game->currentlyActing()->getManaPool()->pay(cost); + cost->doPayExtra(); + } + if (needsTapping && source->isInPlay()) source->tap(); + fireAbility(); + + return 1; + +} + +int ActivatedAbility::reactToTargetClick(Targetable * object){ + if (!isReactingToTargetClick(object)) return 0; + if (cost){ + if (object->typeAsTarget() == TARGET_CARD) cost->setExtraCostsAction(this, (MTGCardInstance *) object); + if (!cost->isExtraPaymentSet()){ + game->waitForExtraPayment = cost->extraCosts; + return 0; + } + game->currentlyActing()->getManaPool()->pay(cost); + cost->doPayExtra(); + } + if (needsTapping && source->isInPlay()) source->tap(); + fireAbility(); + return 1; + +} + +ostream& ActivatedAbility::toString(ostream& out) const +{ + out << "ActivatedAbility ::: playerturnonly : " << playerturnonly + << " ; needsTapping : " << needsTapping + << " ("; + return MTGAbility::toString(out) << ")"; +} + + +//The whole targetAbility mechanism is messed up, mainly because of its interactions with +// the ActionLayer, GameObserver, and parent class ActivatedAbility. +// Currently choosing a target is a complete different mechanism for put into play and for other abilities. +// It probably shouldn't be the case. + +TargetAbility::TargetAbility(int id, MTGCardInstance * card, TargetChooser * _tc,ManaCost * _cost, int _playerturnonly,int tap):ActivatedAbility(id, card,_cost,_playerturnonly, tap){ + tc = _tc; + ability = NULL; +} + +TargetAbility::TargetAbility(int id, MTGCardInstance * card,ManaCost * _cost, int _playerturnonly,int tap):ActivatedAbility(id, card,_cost,_playerturnonly, tap){ + tc = NULL; + ability = NULL; +} + + +int TargetAbility::reactToTargetClick(Targetable * object){ + if (object->typeAsTarget() == TARGET_CARD) return reactToClick((MTGCardInstance *)object); + if (waitingForAnswer){ + if (tc->toggleTarget(object) == TARGET_OK_FULL){ + waitingForAnswer = 0; + game->mLayers->actionLayer()->setCurrentWaitingAction(NULL); + return ActivatedAbility::reactToClick(source); + } + return 1; + } + return 0; +} + + +int TargetAbility::reactToClick(MTGCardInstance * card){ + if (!waitingForAnswer) { + if (isReactingToClick(card)){ + waitingForAnswer = 1; + game->mLayers->actionLayer()->setCurrentWaitingAction(this); + tc->initTargets(); + return 1; + } + }else{ + if (card == source && (tc->targetsReadyCheck() == TARGET_OK || tc->targetsReadyCheck() == TARGET_OK_FULL)){ + waitingForAnswer = 0; + game->mLayers->actionLayer()->setCurrentWaitingAction(NULL); + return ActivatedAbility::reactToClick(source); + }else{ + if (tc->toggleTarget(card) == TARGET_OK_FULL){ + int result = ActivatedAbility::reactToClick(source); + if (result) { + waitingForAnswer = 0; + game->mLayers->actionLayer()->setCurrentWaitingAction(NULL); + } + return result; + } + return 1; + } + } + return 0; +} + +void TargetAbility::Render(){ + //TODO ? +} + + +int TargetAbility::resolve(){ + Targetable * t = tc->getNextTarget(); + if (t && ability){ + ability->target = t; + return ability->resolve(); + } + return 0; +} + +const char * TargetAbility::getMenuText(){ + if (ability) return ability->getMenuText(); + return ActivatedAbility::getMenuText(); +} + +TargetAbility::~TargetAbility(){ + if (!isClone) SAFE_DELETE(ability); +} + +ostream& TargetAbility::toString(ostream& out) const +{ + out << "TargetAbility ::: ("; + return ActivatedAbility::toString(out) << ")"; +} + +// + + +TriggeredAbility::TriggeredAbility(int id, MTGCardInstance * card, Targetable * _target):MTGAbility(id,card, _target){ +} + + +TriggeredAbility::TriggeredAbility(int id, MTGCardInstance * card):MTGAbility(id,card){ +} + +int TriggeredAbility::receiveEvent(WEvent * e){ + if (triggerOnEvent(e)){ + fireAbility(); + return 1; + } + return 0; +} + +void TriggeredAbility::Update(float dt){ + if (trigger()) fireAbility(); +} + +ostream& TriggeredAbility::toString(ostream& out) const +{ + out << "TriggeredAbility ::: ("; + return MTGAbility::toString(out) << ")"; +} + + +// +InstantAbility::InstantAbility(int _id, MTGCardInstance * source):MTGAbility(_id, source){ + init = 0; +} + +void InstantAbility::Update(float dt){ + if (!init){ + init = resolve(); + } +} + +InstantAbility::InstantAbility(int _id, MTGCardInstance * source, Damageable * _target):MTGAbility(_id, source, _target){ + init = 0; +} + + + +//Instant abilities last generally until the end of the turn +int InstantAbility::testDestroy(){ + int newPhase = game->getCurrentGamePhase(); + if (newPhase != currentPhase && newPhase == Constants::MTG_PHASE_AFTER_EOT) return 1; + currentPhase = newPhase; + return 0; + +} + +ostream& InstantAbility::toString(ostream& out) const +{ + out << "InstantAbility ::: init : " << init + << " ("; + return MTGAbility::toString(out) << ")"; +} + +bool ListMaintainerAbility::canTarget(MTGGameZone * zone){ + if (tc) return tc->targetsZone(zone); + for (int i = 0; i < 2; i++){ + Player * p = game->players[i]; + if (zone == p->game->inPlay) return true; + } + return false; +} + +void ListMaintainerAbility::updateTargets(){ + //remove invalid ones + map temp; + for (map::iterator it=cards.begin(); it != cards.end(); ++it){ + MTGCardInstance * card = (*it).first; + if (!canBeInList(card)) temp[card] = true; + } + + for (map::iterator it=temp.begin(); it != temp.end(); ++it){ + MTGCardInstance * card = (*it).first; + cards.erase(card); + removed(card); + } + + temp.clear(); + + //add New valid ones + for (int i = 0; i < 2; i++){ + Player * p = game->players[i]; + MTGGameZone * zones[] = {p->game->inPlay,p->game->graveyard,p->game->hand,p->game->library}; + for (int k = 0; k < 4; k++){ + MTGGameZone * zone = zones[k]; + if (canTarget(zone)){ + for (int j = 0; j < zone->nb_cards; j++){ + if (canBeInList(zone->cards[j])){ + if(cards.find(zone->cards[j]) == cards.end()){ + temp[zone->cards[j]] = true; + } + } + } + } + } + } + + for (map::iterator it=temp.begin(); it != temp.end(); ++it){ + MTGCardInstance * card = (*it).first; + cards[card] = true; + added(card); + } + + temp.clear(); + + for (int i = 0; i < 2; ++i){ + Player * p = game->players[i]; + if (!players[p] && canBeInList(p)){ + players[p] = true; + added(p); + }else if (players[p] && !canBeInList(p)){ + players[p] = false; + removed(p); + } + } + +} + +void ListMaintainerAbility::Update(float dt){ + updateTargets(); +} + +//Destroy the spell -> remove all targets +int ListMaintainerAbility::destroy(){ + map::iterator it = cards.begin(); + + while ( it!=cards.end()){ + MTGCardInstance * card = (*it).first; + cards.erase(card); + removed(card); + it = cards.begin(); + } + return 1; +} + +ostream& ListMaintainerAbility::toString(ostream& out) const +{ + out << "ListMaintainerAbility ::: ("; + return MTGAbility::toString(out) << ")"; +} + + + +TriggerAtPhase::TriggerAtPhase(int id, MTGCardInstance * source, Targetable * target,int _phaseId, int who):TriggeredAbility(id, source,target),phaseId(_phaseId),who(who){ + GameObserver * g = GameObserver::GetInstance(); + newPhase = g->getCurrentGamePhase(); + currentPhase = newPhase; +} + +int TriggerAtPhase::trigger(){ + GameObserver * g = GameObserver::GetInstance(); + int result = 0; + if (currentPhase != newPhase && newPhase == phaseId){ + result = 0; + switch(who){ + case 1: + if(g->currentPlayer == source->controller()) result = 1; + break; + case -1: + if(g->currentPlayer != source->controller()) result = 1; + break; + case -2: + if(source->target) { + if (g->currentPlayer == source->target->controller()) result = 1; + }else { + if(g->currentPlayer == source->controller()) result = 1; + } + break; + default: + result = 1; + break; + } + } + return result; +} + +TriggerAtPhase* TriggerAtPhase::clone() const{ + TriggerAtPhase * a = NEW TriggerAtPhase(*this); + a->isClone = 1; + return a; +} + +TriggerNextPhase::TriggerNextPhase(int id, MTGCardInstance * source, Targetable * target,int _phaseId,int who):TriggerAtPhase(id, source,target,_phaseId, who){ + destroyActivated = 0; +} + +int TriggerNextPhase::testDestroy(){ + if (newPhase <= phaseId) destroyActivated = 1; + if ( newPhase > phaseId && destroyActivated){ + return 1; + } + return 0; +} + +TriggerNextPhase* TriggerNextPhase::clone() const{ + TriggerNextPhase * a = NEW TriggerNextPhase(*this); + a->isClone = 1; + return a; +} + +GenericTriggeredAbility::GenericTriggeredAbility(int id, MTGCardInstance * _source, TriggeredAbility * _t, MTGAbility * a , MTGAbility * dc, Targetable * _target ): TriggeredAbility(id, _source,_target){ + if (!target) target = source; + t = _t; + ability = a; + destroyCondition = dc; + + t->source = source; + t->target = target; + ability->source = source; + ability->target = target; + if (destroyCondition){ + destroyCondition->source = source; + destroyCondition->target = target;; + } +} + +int GenericTriggeredAbility::trigger(){ + return t->trigger(); +} + + +int GenericTriggeredAbility::triggerOnEvent(WEvent * e){ + return t->triggerOnEvent(e); +} + +void GenericTriggeredAbility::Update(float dt){ + GameObserver * g = GameObserver::GetInstance(); + int newPhase = g->getCurrentGamePhase(); + t->newPhase = newPhase; + TriggeredAbility::Update(dt); + t->currentPhase = newPhase; +} + +int GenericTriggeredAbility::resolve(){ + if (ability->oneShot) return ability->resolve(); + MTGAbility * clone = ability->clone(); + clone->addToGame(); + return 1; +} + +int GenericTriggeredAbility::testDestroy(){ + if (!TriggeredAbility::testDestroy()) return 0; + if (destroyCondition) return (destroyCondition->testDestroy()); + return t->testDestroy(); +} + +GenericTriggeredAbility::~GenericTriggeredAbility(){ + if (!isClone){ + delete t; + delete ability; + SAFE_DELETE(destroyCondition); + } +} + + const char * GenericTriggeredAbility::getMenuText(){ + return ability->getMenuText(); + } + +GenericTriggeredAbility* GenericTriggeredAbility::clone() const{ + GenericTriggeredAbility * a = NEW GenericTriggeredAbility(*this); + a->isClone = 1; + return a; +} + +/*Mana Producers (lands) +//These have a reactToClick function, and therefore two manaProducers on the same card conflict with each other +//That means the player has to choose one. although that is perfect for cards such as birds of paradise or badlands, +other solutions need to be provided for abilities that add mana (ex: mana flare) +*/ + + +AManaProducer::AManaProducer(int id, MTGCardInstance * card, Targetable * t, ManaCost * _output, ManaCost * _cost , int doTap, int who):ActivatedAbilityTP(id, card,t,_cost,doTap,who){ + + LOG("==Creating ManaProducer Object"); + aType = MTGAbility::MANA_PRODUCER; + cost = _cost; + output = _output; + + menutext = ""; + + + + LOG("==ManaProducer Object Creation successful !"); +} + + int AManaProducer::isReactingToClick(MTGCardInstance * _card, ManaCost * mana){ + int result = 0; + if (!mana) mana = game->currentlyActing()->getManaPool(); + if (_card == source && (!tap || !source->isTapped()) && game->currentlyActing()->game->inPlay->hasCard(source) && (source->hasType(Subtypes::TYPE_LAND) || !tap || !source->hasSummoningSickness()) ){ + if (!cost || mana->canAfford(cost)) result = 1; + } + return result; + } + + int AManaProducer::resolve(){ Targetable * _target = getTarget(); Player * player; if (_target){ @@ -2215,87 +2240,87 @@ AManaProducer::AManaProducer(int id, MTGCardInstance * card, Targetable * t, Man player = ((MTGCardInstance *)_target)->controller(); }else{ player = (Player *) _target; - } - player->getManaPool()->add(output,source); - return 1; - } - return 0; - } - - int AManaProducer::reactToClick(MTGCardInstance * _card){ - if (!isReactingToClick( _card)) return 0; - if (cost){ - cost->setExtraCostsAction(this, _card); - if (!cost->isExtraPaymentSet()){ - GameObserver::GetInstance()->waitForExtraPayment = cost->extraCosts; - return 0; - } - GameObserver::GetInstance()->currentlyActing()->getManaPool()->pay(cost); - cost->doPayExtra(); - } - if (tap) source->tap(); - - if (options[Options::SFXVOLUME].number > 0){ - JSample * sample = resources.RetrieveSample("mana.wav"); - if (sample) JSoundSystem::GetInstance()->PlaySample(sample); - } - return resolve(); - } - - - const char * AManaProducer::getMenuText(){ - if (menutext.size())return menutext.c_str(); - menutext = _("Add "); - char buffer[128]; - int alreadyHasOne = 0; - for (int i= 0; i < 6; i++){ - int value = output->getCost(i); - if (value){ - if (alreadyHasOne) menutext.append(","); - sprintf(buffer, "%i ", value); - menutext.append(buffer); - switch (i){ - case Constants::MTG_COLOR_RED: - menutext.append(_("red")); - break; - case Constants::MTG_COLOR_BLUE: - menutext.append(_("blue")); - break; - case Constants::MTG_COLOR_GREEN: - menutext.append(_("green")); - break; - case Constants::MTG_COLOR_WHITE: - menutext.append(_("white")); - break; - case Constants::MTG_COLOR_BLACK: - menutext.append(_("black")); - break; - default: - break; - } - alreadyHasOne = 1; - } - } - menutext.append(_(" mana")); - return menutext.c_str(); - } - - - AManaProducer::~AManaProducer(){ - if (isClone) return; - LOG("==Destroying ManaProducer Object"); - SAFE_DELETE(cost); - SAFE_DELETE(output); - LOG("==Destroying ManaProducer Object Successful!"); - } - - AManaProducer * AManaProducer::clone() const{ - AManaProducer * a = NEW AManaProducer(*this); - a->isClone = 1; - return a; - } - - + } + player->getManaPool()->add(output,source); + return 1; + } + return 0; + } + + int AManaProducer::reactToClick(MTGCardInstance * _card){ + if (!isReactingToClick( _card)) return 0; + if (cost){ + cost->setExtraCostsAction(this, _card); + if (!cost->isExtraPaymentSet()){ + GameObserver::GetInstance()->waitForExtraPayment = cost->extraCosts; + return 0; + } + GameObserver::GetInstance()->currentlyActing()->getManaPool()->pay(cost); + cost->doPayExtra(); + } + if (tap) source->tap(); + + if (options[Options::SFXVOLUME].number > 0){ + JSample * sample = resources.RetrieveSample("mana.wav"); + if (sample) JSoundSystem::GetInstance()->PlaySample(sample); + } + return resolve(); + } + + + const char * AManaProducer::getMenuText(){ + if (menutext.size())return menutext.c_str(); + menutext = _("Add "); + char buffer[128]; + int alreadyHasOne = 0; + for (int i= 0; i < 6; i++){ + int value = output->getCost(i); + if (value){ + if (alreadyHasOne) menutext.append(","); + sprintf(buffer, "%i ", value); + menutext.append(buffer); + switch (i){ + case Constants::MTG_COLOR_RED: + menutext.append(_("red")); + break; + case Constants::MTG_COLOR_BLUE: + menutext.append(_("blue")); + break; + case Constants::MTG_COLOR_GREEN: + menutext.append(_("green")); + break; + case Constants::MTG_COLOR_WHITE: + menutext.append(_("white")); + break; + case Constants::MTG_COLOR_BLACK: + menutext.append(_("black")); + break; + default: + break; + } + alreadyHasOne = 1; + } + } + menutext.append(_(" mana")); + return menutext.c_str(); + } + + + AManaProducer::~AManaProducer(){ + if (isClone) return; + LOG("==Destroying ManaProducer Object"); + SAFE_DELETE(cost); + SAFE_DELETE(output); + LOG("==Destroying ManaProducer Object Successful!"); + } + + AManaProducer * AManaProducer::clone() const{ + AManaProducer * a = NEW AManaProducer(*this); + a->isClone = 1; + return a; + } + + ActivatedAbilityTP::ActivatedAbilityTP(int id, MTGCardInstance * card, Targetable * _target, ManaCost * cost, int doTap, int who):ActivatedAbility(id,card,cost,0,doTap),who(who){ if (_target) target = _target; diff --git a/projects/mtg/src/ReplacementEffects.cpp b/projects/mtg/src/ReplacementEffects.cpp index 4c03773e7..04b108d3c 100644 --- a/projects/mtg/src/ReplacementEffects.cpp +++ b/projects/mtg/src/ReplacementEffects.cpp @@ -2,10 +2,10 @@ #include "../include/ReplacementEffects.h" #include "../include/MTGCardInstance.h" #include "../include/TargetChooser.h" -#include "../include/Damage.h" -REDamagePrevention::REDamagePrevention(MTGAbility * source, TargetChooser *tcSource, TargetChooser *tcTarget, int damage, bool oneShot):source(source), tcSource(tcSource), tcTarget(tcTarget), damage(damage), oneShot(oneShot){ + +REDamagePrevention::REDamagePrevention(MTGAbility * source, TargetChooser *tcSource, TargetChooser *tcTarget, int damage, bool oneShot, int typeOfDamage):source(source), tcSource(tcSource), tcTarget(tcTarget), damage(damage), oneShot(oneShot), typeOfDamage(typeOfDamage){ } WEvent * REDamagePrevention::replace (WEvent *event){ @@ -14,6 +14,7 @@ WEvent * REDamagePrevention::replace (WEvent *event){ WEventDamage * e = dynamic_cast(event); if (!e) return event; Damage *d = e->damage; + if (d->typeOfDamage != typeOfDamage && typeOfDamage != DAMAGE_ALL_TYPES) return event; if ((!tcSource || tcSource->canTarget(d->source)) && (!tcTarget || tcTarget->canTarget(d->target)) ){ diff --git a/projects/mtg/src/TargetChooser.cpp b/projects/mtg/src/TargetChooser.cpp index d45d6bca4..70d2f845b 100644 --- a/projects/mtg/src/TargetChooser.cpp +++ b/projects/mtg/src/TargetChooser.cpp @@ -7,7 +7,7 @@ -TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInstance * card){ +TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInstance * card, MTGAbility * ability){ if (!s.size()) return NULL; int zones[10]; @@ -15,6 +15,13 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta size_t found; bool other = false; + found = s.find("mytgt"); + if (found == 0){ + MTGCardInstance * target = card->target; + if (ability) target = (MTGCardInstance *) (ability->target); + return NEW CardTargetChooser(target,card); + }; + found = s.find("other "); if (found == 0){ other = true;