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