diff --git a/projects/mtg/include/ActionStack.h b/projects/mtg/include/ActionStack.h index b3ae08b7d..7cebf72a8 100644 --- a/projects/mtg/include/ActionStack.h +++ b/projects/mtg/include/ActionStack.h @@ -172,6 +172,7 @@ class ActionStack :public GuiLayer{ int addAction(Interruptible * interruptible); Spell * addSpell(MTGCardInstance* card, TargetChooser * tc, ManaCost * mana, int payResult, int storm); int AddNextGamePhase(); + int AddNextCombatStep(); int addPutInGraveyard(MTGCardInstance * card); int addDraw(Player * player, int nbcards = 1); int addDamage(MTGCardInstance * _source, Damageable * target, int _damage); diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index 8907bf26b..76f98f0eb 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -60,6 +60,11 @@ public: intValue = target->equipment; }else if (s == "manacost"){ intValue = target->getManaCost()->getConvertedCost(); + }else if (s == "sunburst"){ + intValue = 0; + if(card && card->previous && card->previous->previous){ + intValue = card->previous->previous->sunburst; + } }else if (s == "lifetotal"){ intValue = target->controller()->life; }else if (s == "odcount"){ @@ -188,11 +193,240 @@ public: } }; +class TrCardTappedformana:public TriggeredAbility{ +public: + TargetChooser * tc; + bool tap; + TrCardTappedformana(int id, MTGCardInstance * source, TargetChooser * tc, bool tap = true):TriggeredAbility(id,source), tc(tc),tap(tap){} + + int resolve(){ + return 0; //This is a trigger, this function should not be called + } + + int triggerOnEvent(WEvent * event){ + WEventCardTappedForMana * 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; + } + + ~TrCardTappedformana(){ + SAFE_DELETE(tc); + } + + TrCardTappedformana * clone() const{ + TrCardTappedformana * a = NEW TrCardTappedformana(*this); + a->isClone = 1; + return a; + } +}; + +class TrCardAttackedNotBlocked:public TriggeredAbility{ +public: + TargetChooser * tc; + TrCardAttackedNotBlocked(int id, MTGCardInstance * source, TargetChooser * tc):TriggeredAbility(id,source), tc(tc){} + + int resolve(){ + return 0; //This is a trigger, this function should not be called + } + + int triggerOnEvent(WEvent * event){ + WEventCardAttackedNotBlocked * e = dynamic_cast(event); + if (!e) return 0; + if(e->card->didattacked < 1) return 0; + if(e->card->blocked) return 0; + if (!tc->canTarget(e->card)) return 0; + return 1; + } + + ~TrCardAttackedNotBlocked(){ + SAFE_DELETE(tc); + } + + TrCardAttackedNotBlocked * clone() const{ + TrCardAttackedNotBlocked * a = NEW TrCardAttackedNotBlocked(*this); + a->isClone = 1; + return a; + } +}; + +class TrCardAttackedBlocked:public TriggeredAbility{ +public: + TargetChooser * tc; + TargetChooser * fromTc; + TrCardAttackedBlocked(int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL ):TriggeredAbility(id,source), tc(tc), fromTc(fromTc){} + + int resolve(){ + return 0; //This is a trigger, this function should not be called + } + + int triggerOnEvent(WEvent * event){ + WEventCardAttackedBlocked * e = dynamic_cast(event); + if (!e) return 0; + if(e->card->didattacked < 1) return 0; + if(!e->card->blocked) return 0; + if (fromTc && !fromTc->canTarget(e->card->getNextOpponent())) return 0; + if (!tc->canTarget(e->card)) return 0; + return 1; + } + + ~TrCardAttackedBlocked(){ + SAFE_DELETE(tc); + SAFE_DELETE(fromTc); + } + + TrCardAttackedBlocked * clone() const{ + TrCardAttackedBlocked * a = NEW TrCardAttackedBlocked(*this); + a->isClone = 1; + return a; + } +}; + +class TrCardAttacked:public TriggeredAbility{ +public: + TargetChooser * tc; + TrCardAttacked(int id, MTGCardInstance * source, TargetChooser * tc):TriggeredAbility(id,source), tc(tc){} + + int resolve(){ + return 0; //This is a trigger, this function should not be called + } + + int triggerOnEvent(WEvent * event){ + WEventCardAttacked * e = dynamic_cast(event); + if (!e) return 0; + if(e->card->didattacked < 1) return 0; + if (!tc->canTarget(e->card)) return 0; + return 1; + } + + ~TrCardAttacked(){ + SAFE_DELETE(tc); + } + + TrCardAttacked * clone() const{ + TrCardAttacked * a = NEW TrCardAttacked(*this); + a->isClone = 1; + return a; + } +}; + +class TrCardBlocked:public TriggeredAbility{ +public: + TargetChooser * tc; + TargetChooser * fromTc; + TrCardBlocked(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){ + WEventCardBlocked * e = dynamic_cast(event); + if (!e) return 0; + //if(e->card->didblocked < 1) return 0; + if (fromTc && !fromTc->canTarget(e->card->getNextOpponent())) return 0; + if (!tc->canTarget(e->card)) return 0; + return 1; + } + + ~TrCardBlocked(){ + SAFE_DELETE(tc); + SAFE_DELETE(fromTc); + } + + TrCardBlocked * clone() const{ + TrCardBlocked * a = NEW TrCardBlocked(*this); + a->isClone = 1; + return a; + } +}; + +class TrcardDrawn:public TriggeredAbility{ +public: + TargetChooser * tc; + TrcardDrawn(int id, MTGCardInstance * source, TargetChooser * tc):TriggeredAbility(id,source), tc(tc){} + + int resolve(){ + return 0; //This is a trigger, this function should not be called + } + + int triggerOnEvent(WEvent * event){ + WEventcardDraw * e = dynamic_cast(event); + if (!e) return 0; + if (!tc->canTarget(e->player))return 0; + return 1; + } + + ~TrcardDrawn(){ + SAFE_DELETE(tc); + } + + TrcardDrawn * clone() const{ + TrcardDrawn * a = NEW TrcardDrawn(*this); + a->isClone = 1; + return a; + } +}; + +class TrCardSacrificed:public TriggeredAbility{ +public: + TargetChooser * tc; + TrCardSacrificed(int id, MTGCardInstance * source, TargetChooser * tc):TriggeredAbility(id,source), tc(tc){} + + int resolve(){ + return 0; //This is a trigger, this function should not be called + } + + int triggerOnEvent(WEvent * event){ + WEventCardSacrifice * e = dynamic_cast(event); + if (!e) return 0; + if (!tc->canTarget(e->card)) return 0; + return 1; + } + + ~TrCardSacrificed(){ + SAFE_DELETE(tc); + } + + TrCardSacrificed * clone() const{ + TrCardSacrificed * a = NEW TrCardSacrificed(*this); + a->isClone = 1; + return a; + } +}; + +class TrCardDiscarded:public TriggeredAbility{ +public: + TargetChooser * tc; + TrCardDiscarded(int id, MTGCardInstance * source, TargetChooser * tc):TriggeredAbility(id,source), tc(tc){} + + int resolve(){ + return 0; //This is a trigger, this function should not be called + } + int triggerOnEvent(WEvent * event){ + WEventCardDiscard * e = dynamic_cast(event); + if (!e) return 0; + if (!tc->canTarget(e->card)) return 0; + return 1; + } + ~TrCardDiscarded(){ + SAFE_DELETE(tc); + } + TrCardDiscarded * clone() const{ + TrCardDiscarded * a = NEW TrCardDiscarded(*this); + a->isClone = 1; + return a; + } +}; + class TrDamaged:public TriggeredAbility{ public: TargetChooser * tc; TargetChooser * fromTc; - TrDamaged (int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL):TriggeredAbility(id,source), tc(tc), fromTc(fromTc){} + int type;//this allows damagenoncombat and combatdamage to share this trigger + TrDamaged (int id, MTGCardInstance * source, TargetChooser * tc, TargetChooser * fromTc = NULL,int type = 0):TriggeredAbility(id,source), tc(tc), fromTc(fromTc), type(type){} int resolve(){ return 0; //This is a trigger, this function should not be called @@ -203,6 +437,8 @@ public: if (!e) return 0; if(!tc->canTarget(e->damage->target)) return 0; if (fromTc && !fromTc->canTarget(e->damage->source)) return 0; + if (type == 1 && e->damage->typeOfDamage != DAMAGE_COMBAT ) return 0; + if (type == 2 && e->damage->typeOfDamage == DAMAGE_COMBAT ) return 0; return 1; } @@ -496,6 +732,102 @@ class AACopier:public ActivatedAbility{ } }; +//cloning...this makes a token thats a copy of the target. +class AACloner:public ActivatedAbility{ + public: + int who; + string with; + listawith; + listcolors; + AACloner(int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost=NULL, int who = 0,string with =""):ActivatedAbility(_id,_source,_cost,0,0), who(who){ + target = _target; + source = _source; + + for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++){ + size_t found = with.find(Constants::MTGBasicAbilities[j]); + if (found != string::npos){ + awith.push_back(j); + } + } + for (int j = 0; j < Constants::MTG_NB_COLORS; j++){ + size_t found = with.find(Constants::MTGColorStrings[j]); + if (found != string::npos){ + colors.push_back(j); + } + } + + } + + int resolve(){ + MTGCardInstance * _target = (MTGCardInstance *) target; + if(_target && !_target->isToken){ + MTGCardInstance * myClone; + MTGCard * clone = GameApp::collection->getCardById(_target->getId()); + myClone = NULL; + if(who != 1) myClone = NEW MTGCardInstance(clone,source->controller()->game); + if(who == 1) myClone = NEW MTGCardInstance(clone,source->controller()->opponent()->game); + if(who != 1) source->controller()->game->temp->addCard(myClone); + else source->controller()->opponent()->game->temp->addCard(myClone); + Spell * spell = NEW Spell(myClone); + spell->resolve(); + spell->source->isToken = 1; + spell->source->fresh = 1; + list::iterator it; + for ( it=awith.begin() ; it != awith.end(); it++ ){ + spell->source->basicAbilities[*it] = 1; + } + for ( it=colors.begin() ; it != colors.end(); it++ ){ + spell->source->setColor(*it); + } + delete spell; + return 1; + } + if(_target && _target->isToken){ + MTGCardInstance * myClone; + MTGCardInstance * clone = _target; + myClone = NULL; + if(who != 1) myClone = NEW MTGCardInstance(clone,source->controller()->game); + if(who == 1) myClone = NEW MTGCardInstance(clone,source->controller()->opponent()->game); + if(who != 1) source->controller()->game->temp->addCard(myClone); + else source->controller()->opponent()->game->temp->addCard(myClone); + Spell * spell = NEW Spell(myClone); + spell->resolve(); + spell->source->isToken = 1; + spell->source->fresh = 1; + list::iterator it; + for ( it=awith.begin() ; it != awith.end(); it++ ){ + spell->source->basicAbilities[*it] = 1; + } + for ( it=colors.begin() ; it != colors.end(); it++ ){ + spell->source->setColor(*it); + } + delete spell; + return 1; + } + return 0; + } + + const char * getMenuText(){ + if(who == 1) return "Clone For Opponent"; + return "Clone"; + } + + virtual ostream& toString(ostream& out) const + { + out << "AACloner ::: with : ?" // << abilities + << " ("; + return ActivatedAbility::toString(out) << ")"; + } + + + AACloner * clone() const{ + AACloner * a = NEW AACloner(*this); + a->isClone = 1; + return a; + } + ~AACloner(){} +}; + class AAMover:public ActivatedAbility{ public: string destination; @@ -652,6 +984,9 @@ class ACycle:public ActivatedAbility{ } int resolve(){ + WEvent * e = NEW WEventCardDiscard(source); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); source->controller()->game->putInGraveyard(source); source->controller()->game->drawFromLibrary(); return 1; @@ -670,6 +1005,37 @@ class ACycle:public ActivatedAbility{ }; +//ninjutsu + +class ANinja:public ActivatedAbility{ + public: + ANinja(int _id, MTGCardInstance * card,Targetable * _target):ActivatedAbility(_id, card){ + target = _target; + } + + int resolve(){ + MTGCardInstance * copy = source->controller()->game->putInZone(source, source->controller()->game->hand, source->controller()->game->temp); + Spell * spell = NEW Spell(copy); + spell->resolve(); + MTGCardInstance * newcard = spell->source; + newcard->summoningSickness = 0; + newcard->tap(); + newcard->setAttacker(1); + delete spell; + return 1; + } + + const char * getMenuText(){ + return "Ninjutsu"; + } + + ANinja * clone() const{ + ANinja * a = NEW ANinja(*this); + a->isClone = 1; + return a; + } +}; + //Drawer, allows to draw a card for a cost: @@ -845,16 +1211,18 @@ public: 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){ + int who; + ATokenCreator(int _id,MTGCardInstance * _source,ManaCost * _cost, int tokenId, int _doTap, WParsedInt * multiplier = NULL,int who = 0):ActivatedAbility(_id,_source,_cost,0,_doTap), tokenId(tokenId), multiplier(multiplier), who(who){ 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){ + ATokenCreator(int _id,MTGCardInstance * _source,ManaCost * _cost, string sname, string stypes,int _power,int _toughness, string sabilities, int _doTap, WParsedInt * multiplier = NULL,int who = 0):ActivatedAbility(_id,_source,_cost,0,_doTap), multiplier(multiplier),who(who){ power = _power; toughness = _toughness; name = sname; + who = who; 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; @@ -864,7 +1232,7 @@ public: 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]); @@ -885,8 +1253,8 @@ public: types.push_back(id); s = ""; } - } - } + } + } int resolve(){ for (int i = 0; i < multiplier->getValue(); ++i){ @@ -907,11 +1275,22 @@ public: myToken->basicAbilities[*it] = 1; } } + if(who == 0 || who != 1){ source->controller()->game->temp->addCard(myToken); Spell * spell = NEW Spell(myToken); spell->resolve(); spell->source->isToken = 1; + spell->source->fresh = 1; delete spell; + }else if (who == 1){ + source->controller()->opponent()->game->temp->addCard(myToken); + Spell * spell = NEW Spell(myToken); + spell->resolve(); + spell->source->owner = spell->source->controller(); + spell->source->isToken = 1; + spell->source->fresh = 1; + delete spell; + } } return 1; } @@ -929,6 +1308,7 @@ public: << " ; power : " << power << " ; toughness : " << toughness << " ; name : " << name + << " ; who : " << who << " ("; return ActivatedAbility::toString(out) << ")"; } @@ -1007,7 +1387,47 @@ public: }; +class AASacDis:public ActivatedAbility{ +public: + int sacrifice; + AASacDis(int _id, MTGCardInstance * _source, MTGCardInstance * _target, int _sacrifice = 0, ManaCost * _cost=NULL):ActivatedAbility(_id,_source,_cost),sacrifice(_sacrifice){ + if (_target) target = _target; + } + int resolve(){ + MTGCardInstance * _target = (MTGCardInstance *) target; + if(_target){ + Player * p = _target->controller(); + Player * owner = _target->owner; + if (sacrifice) + { + WEvent * e = NEW WEventCardSacrifice(_target); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); + p->game->putInGraveyard(_target); + return 1; + } + else + { + WEvent * e = NEW WEventCardDiscard(_target); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); + p->game->putInGraveyard(_target); + return 1; + } + } + return 0; + } + const char * getMenuText(){ + if(sacrifice) return "Sacrifice"; + else return "Discard"; + } + AASacDis * clone() const{ + AASacDis * a = NEW AASacDis(*this); + a->isClone = 1; + return a; + } +}; /*Changes one of the basic abilities of target source : spell @@ -2075,6 +2495,8 @@ public: a = NULL; SAFE_DELETE(tc); } + + int removeFromGame(){ return removeAbilityFromGame(); } @@ -2093,8 +2515,8 @@ public: match = td->match(source); if (match > 0){ addAbilityToGame(); - }else{ - removeAbilityFromGame(); + }else{ + removeAbilityFromGame(); } if (ability->oneShot) a = NULL; //allows to call the effect several times return 1; @@ -2278,6 +2700,7 @@ AADamager(int _id, MTGCardInstance * _source, Targetable * _target, WParsedInt * }; + //prevent next damage class AADamagePrevent:public ActivatedAbilityTP{ public: @@ -2435,6 +2858,56 @@ class AAFrozen:public ActivatedAbility{ return a; } }; + +/* switch power and toughness of target */ +class ATwist:public InstantAbility{ + public: + int oldpower; + int oldtoughness; + ATwist(int _id, MTGCardInstance * _source, MTGCardInstance * _target): InstantAbility(_id, _source, _target){ + target = _target; + } + + int resolve(){ + MTGCardInstance * _target = (MTGCardInstance *) target; + if (_target){ + while (_target->next) _target=_target->next; //This is for cards such as rampant growth + oldpower = _target->power; + oldtoughness = _target->toughness; + + _target->addToToughness(oldpower); + _target->addToToughness(-oldtoughness); + _target->power = oldtoughness; + + } + return 1; + } + + int destroy(){ + MTGCardInstance * _target = (MTGCardInstance *) target; + if (_target){ + while (_target->next) _target=_target->next; //This is for cards such as rampant growth + oldpower = _target->power; + oldtoughness = _target->toughness; + + _target->addToToughness(oldpower); + _target->addToToughness(-oldtoughness); + _target->power = oldtoughness; + + } + return 1; + } + + const char * getMenuText(){ + return "Switch"; + } + ATwist * clone() const{ + ATwist * a = NEW ATwist(*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{ @@ -2844,6 +3317,26 @@ public: return a;} ~ATransformerFOREVER(){delete ability; }}; + //switch p/t ueot + class ATwistUEOT: public InstantAbility{ +public: + ATwist * ability; + ATwistUEOT(int id, MTGCardInstance * source, MTGCardInstance * target):InstantAbility(id,source,target){ + ability = NEW ATwist(id,source,target);} + int resolve(){ + ATwist * a = ability->clone(); + GenericInstantAbility * wrapper = NEW GenericInstantAbility(1,source,(Damageable *)(this->target),a); + wrapper->addToGame(); + return 1;} + ATwistUEOT * clone() const{ + ATwistUEOT * a = NEW ATwistUEOT(*this); + a->ability = this->ability->clone(); + a->isClone = 1; + return a;} + ~ATwistUEOT(){ + delete ability; + }}; + //becomes ability //Adds types/abilities/P/T to a card (aura) class ABecomes:public MTGAbility{ @@ -2971,8 +3464,9 @@ class APreventAllCombatDamage:public MTGAbility{ public: string to, from; REDamagePrevention * re; + int type; - APreventAllCombatDamage(int id,MTGCardInstance * source,string to,string from):MTGAbility(id,source),to(to),from(from){ + APreventAllCombatDamage(int id,MTGCardInstance * source,string to,string from,int type = 0):MTGAbility(id,source),to(to),from(from),type(type){ re = NULL; } @@ -2986,7 +3480,17 @@ class APreventAllCombatDamage:public MTGAbility{ if (toTc) toTc->targetter = NULL; TargetChooser *fromTc = tcf.createTargetChooser(from,source,this); if (fromTc) fromTc->targetter = NULL; + if(type != 1 && type != 2){//not adding this creates a memory leak. re = NEW REDamagePrevention (this, fromTc, toTc, -1, false, DAMAGE_COMBAT); + } + if(type == 1){ + re = NULL; + re = NEW REDamagePrevention (this, fromTc, toTc, -1, false, DAMAGE_ALL_TYPES); + } + if(type == 2){ + re = NULL; + re = NEW REDamagePrevention (this, fromTc, toTc, -1, false, DAMAGE_OTHER); + } game->replacementEffects->add(re); return MTGAbility::addToGame(); } @@ -3010,10 +3514,10 @@ 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 type; + APreventAllCombatDamageUEOT(int id,MTGCardInstance * source,string to, string from,int type = 0):InstantAbility(id,source){ + ability = NEW APreventAllCombatDamage(id,source,to, from,type); } - int resolve(){ APreventAllCombatDamage * a = ability->clone(); @@ -3022,8 +3526,6 @@ public: return 1; } - - int destroy(){ for (size_t i = 0; i < clones.size(); ++i){ clones[i]->forceDestroy = 0; @@ -3036,7 +3538,6 @@ public: return ability->getMenuText(); } - APreventAllCombatDamageUEOT * clone() const{ APreventAllCombatDamageUEOT * a = NEW APreventAllCombatDamageUEOT(*this); a->ability = this->ability->clone(); @@ -3672,7 +4173,7 @@ class AHypnoticSpecter:public MTGAbility{ if (e->damage->source != source) return 0; Player * p = dynamic_cast(e->damage->target); if (!p) return 0; - p->game->discardRandom(p->game->hand); + p->game->discardRandom(p->game->hand,source); return 1; //is this meant to return 0 or 1? } @@ -4456,7 +4957,7 @@ class AARandomDiscarder:public ActivatedAbilityTP{ player = (Player *) _target; } for (int i = 0; i < nbcards; i++){ - player->game->discardRandom(player->game->hand); + player->game->discardRandom(player->game->hand,source); } } return 1; @@ -4562,6 +5063,98 @@ class ARampageAbility:public MTGAbility{ } }; +//flanking ability +class AFlankerAbility:public MTGAbility{ + public: + MTGCardInstance * opponents[20]; + int nbOpponents; + AFlankerAbility(int _id, MTGCardInstance * _source):MTGAbility(_id, _source){ + nbOpponents = 0; + } + int receiveEvent(WEvent * event) { + if (dynamic_cast(event)) { + nbOpponents = 0; + MTGCardInstance * opponent = source->getNextOpponent(); + while (opponent && !opponent->has(Constants::FLANKING) && game->currentlyActing() == source->controller()->opponent()){ + opponents[nbOpponents] = opponent; + nbOpponents ++; + opponent = source->getNextOpponent(opponent); + } + for (int i = 0; i < nbOpponents ; i++){ + opponents[i]->power -= 1; + opponents[i]->addToToughness(-1); + opponents[i]->flanked += 1; + if(opponents[i]->life == 0){opponents[i]->setPower(0);} + } + } + return 1; + } + + virtual ostream& toString(ostream& out) const + { + out << "AFlankerAbility ::: opponents : " << opponents + << " ; nbOpponents : " << nbOpponents + << " ("; + return MTGAbility::toString(out) << ")"; + } + + AFlankerAbility * clone() const{ + AFlankerAbility * a = NEW AFlankerAbility(*this); + a->isClone = 1; + return a; + } +}; + +//Bushido ability +class ABushidoAbility:public MTGAbility{ + public: + MTGCardInstance * opponents[20]; + int nbOpponents; + int PowerModifier; + int ToughnessModifier; + + ABushidoAbility(int _id, MTGCardInstance * _source,int _PowerModifier, int _ToughnessModifier):MTGAbility(_id, _source){ + PowerModifier = _PowerModifier; + ToughnessModifier = _ToughnessModifier; + nbOpponents = 0; + } + int receiveEvent(WEvent * event) { + if (dynamic_cast(event)) { + MTGCardInstance * opponent = source->getNextOpponent(); + if (!opponent) return 0; + source->power += PowerModifier; + source->addToToughness(ToughnessModifier); + while (opponent){ + opponents[nbOpponents] = opponent; + nbOpponents ++; + opponent = source->getNextOpponent(opponent); + } + } + else if (WEventPhaseChange* pe = dynamic_cast(event)) { + if (Constants::MTG_PHASE_AFTER_EOT == pe->to->id && nbOpponents) + { + source->power -= PowerModifier; + source->addToToughness(-ToughnessModifier); + nbOpponents = 0; + } + } + return 1; + } + + virtual ostream& toString(ostream& out) const + { + out << "ABushidoAbility ::: opponents : " << opponents + << " ; nbOpponents : " << nbOpponents + << " ("; + return MTGAbility::toString(out) << ")"; + } + + ABushidoAbility * clone() const{ + ABushidoAbility * a = NEW ABushidoAbility(*this); + a->isClone = 1; + return a; + } +}; //Instant Steal control of a target class AInstantControlSteal: public InstantAbility{ public: @@ -4583,7 +5176,7 @@ class AInstantControlSteal: public InstantAbility{ } return 1; - } + } virtual ostream& toString(ostream& out) const diff --git a/projects/mtg/include/CardDescriptor.h b/projects/mtg/include/CardDescriptor.h index 6c67c7804..492438b1d 100644 --- a/projects/mtg/include/CardDescriptor.h +++ b/projects/mtg/include/CardDescriptor.h @@ -39,6 +39,7 @@ class CardDescriptor: public MTGCardInstance{ int init(); CardDescriptor(); void unsecureSetTapped(int i); + void unsecuresetfresh(int k); void setNegativeSubtype( string value); int counterPower; int counterToughness; diff --git a/projects/mtg/include/ExtraCost.h b/projects/mtg/include/ExtraCost.h index 00fc94e58..a0b022f44 100644 --- a/projects/mtg/include/ExtraCost.h +++ b/projects/mtg/include/ExtraCost.h @@ -70,6 +70,14 @@ public: virtual DiscardRandomCost * clone() const; }; +//a choosen discard +class DiscardCost: public ExtraCost{ +public: + DiscardCost(TargetChooser *_tc = NULL); + virtual int doPay(); + virtual DiscardCost * clone() const; +}; + //tolibrary cost class ToLibraryCost: public ExtraCost{ public: @@ -118,6 +126,15 @@ public: virtual BounceTargetCost * clone() const; }; +//bounce cost +class Ninja: public ExtraCost{ +public: + Ninja(TargetChooser *_tc = NULL); + virtual int isPaymentSet(); + virtual int doPay(); + virtual Ninja * clone() const; +}; + class CounterCost: public ExtraCost{ public: Counter * counter; diff --git a/projects/mtg/include/MTGAbility.h b/projects/mtg/include/MTGAbility.h index db2f8e28e..473e8dcd9 100644 --- a/projects/mtg/include/MTGAbility.h +++ b/projects/mtg/include/MTGAbility.h @@ -89,6 +89,8 @@ class MTGAbility: public ActionElement{ BUYBACK_COST = 9, FLASHBACK_COST = 10, RETRACE_COST = 11, + MTG_COMBATTRIGGERS_RULE = 13, + }; }; diff --git a/projects/mtg/include/MTGCardInstance.h b/projects/mtg/include/MTGCardInstance.h index c2f85062f..886e128fe 100644 --- a/projects/mtg/include/MTGCardInstance.h +++ b/projects/mtg/include/MTGCardInstance.h @@ -29,7 +29,7 @@ class MTGCardInstance: public CardPrimitive, public MTGCard, public Damageable { int untapping; int nb_damages; string sample; - int tapped; + int tapped; int lifeOrig; MTGPlayerCards * belongs_to; @@ -49,11 +49,17 @@ class MTGCardInstance: public CardPrimitive, public MTGCard, public Damageable { int flashedback; int paymenttype; int frozen; + int sunburst; int equipment; int reduxamount; + int flanked; int regenerateTokens; int isToken; int stillInUse(); + int didattacked; + int didblocked; + int notblocked; + int fresh; Player * lastController; MTGGameZone * getCurrentZone(); MTGGameZone * previousZone; @@ -138,6 +144,11 @@ class MTGCardInstance: public CardPrimitive, public MTGCard, public Damageable { void tap(); void attemptUntap(); + void eventattacked(); + void eventattackednotblocked(); + void eventattackedblocked(); + void eventblocked(); + int isInPlay(); JSample * getSample(); diff --git a/projects/mtg/include/MTGDefinitions.h b/projects/mtg/include/MTGDefinitions.h index 365d6fba7..77da9858a 100644 --- a/projects/mtg/include/MTGDefinitions.h +++ b/projects/mtg/include/MTGDefinitions.h @@ -136,8 +136,11 @@ class Constants LEYLINE = 78, PLAYERSHROUD = 79, CONTROLLERSHROUD = 80, + SUNBURST = 81, + FLANKING = 82, + EXILEBURY = 83, - NB_BASIC_ABILITIES = 81, + NB_BASIC_ABILITIES = 84, RARITY_S = 'S', //Special Rarity diff --git a/projects/mtg/include/MTGGameZones.h b/projects/mtg/include/MTGGameZones.h index b1be348d7..97cbe30d8 100644 --- a/projects/mtg/include/MTGGameZones.h +++ b/projects/mtg/include/MTGGameZones.h @@ -155,7 +155,7 @@ class MTGPlayerCards { ~MTGPlayerCards(); void initGame(int shuffle = 1, int draw = 1); void setOwner(Player * player); - void discardRandom(MTGGameZone * from); + void discardRandom(MTGGameZone * from,MTGCardInstance * source); void drawFromLibrary(); void showHand(); void resetLibrary(); diff --git a/projects/mtg/include/MTGRules.h b/projects/mtg/include/MTGRules.h index 475da73d4..589b1898e 100644 --- a/projects/mtg/include/MTGRules.h +++ b/projects/mtg/include/MTGRules.h @@ -86,6 +86,17 @@ class MTGAttackRule:public MTGAbility, public Limitor{ virtual MTGAttackRule * clone() const; }; + +/* handles combat trigger send recieve events*/ +class MTGCombatTriggersRule:public MTGAbility{ + public: + MTGCombatTriggersRule(int _id); + int receiveEvent(WEvent * event); + virtual ostream& toString(ostream& out) const; + int testDestroy(); + virtual MTGCombatTriggersRule * clone() const; +}; + class MTGBlockRule:public MTGAbility{ public: int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); diff --git a/projects/mtg/include/PhaseRing.h b/projects/mtg/include/PhaseRing.h index 2e63b6ef0..a3f187687 100644 --- a/projects/mtg/include/PhaseRing.h +++ b/projects/mtg/include/PhaseRing.h @@ -11,7 +11,7 @@ using namespace std; class Player; -typedef enum { BLOCKERS, ORDER, FIRST_STRIKE, END_FIRST_STRIKE, DAMAGE, END_DAMAGE } CombatStep; +typedef enum { BLOCKERS, TRIGGERS, ORDER, FIRST_STRIKE, END_FIRST_STRIKE, DAMAGE, END_DAMAGE } CombatStep; class Phase{ public: int id; diff --git a/projects/mtg/include/TargetChooser.h b/projects/mtg/include/TargetChooser.h index c787f8b3c..a3f47d99c 100644 --- a/projects/mtg/include/TargetChooser.h +++ b/projects/mtg/include/TargetChooser.h @@ -31,7 +31,8 @@ class TargetChooser: public TargetsList { UNSET = 0, OPPONENT = -1, CONTROLLER = 1, - TARGET_CONTROLLER = 2 + TARGET_CONTROLLER = 2, + OWNER = 3 }; bool other; diff --git a/projects/mtg/include/ThisDescriptor.h b/projects/mtg/include/ThisDescriptor.h index 05a98549d..b83998a83 100644 --- a/projects/mtg/include/ThisDescriptor.h +++ b/projects/mtg/include/ThisDescriptor.h @@ -62,6 +62,21 @@ class ThisEquip:public ThisDescriptor{ ThisEquip(int equipment); }; + +class ThisAttacked:public ThisDescriptor{ + public: + virtual int match(MTGCardInstance * card); + + ThisAttacked(int attack); +}; + +class ThisNotBlocked:public ThisDescriptor{ + public: + virtual int match(MTGCardInstance * card); + + ThisNotBlocked(int unblocked); +}; + class ThisPower:public ThisDescriptor{ public: virtual int match(MTGCardInstance * card); diff --git a/projects/mtg/include/WEvent.h b/projects/mtg/include/WEvent.h index 2fb187dc2..95c353d23 100644 --- a/projects/mtg/include/WEvent.h +++ b/projects/mtg/include/WEvent.h @@ -85,6 +85,14 @@ struct WEventCardTap : public WEventCardUpdate { virtual Targetable * getTarget(int target); }; +struct WEventCardTappedForMana : public WEventCardUpdate { + bool before; + bool after; + WEventCardTappedForMana(MTGCardInstance * card, bool before, bool after); + virtual Targetable * getTarget(int target); +}; + + //Event when a card's "attacker" status changes //before:Player/Planeswalker that card was attacking previously //after: Player/Planeswalker that card is attacking now @@ -94,6 +102,42 @@ struct WEventCreatureAttacker : public WEventCardUpdate { WEventCreatureAttacker(MTGCardInstance * card, Targetable * from, Targetable * to); }; +//event when card attacks. +struct WEventCardAttacked : public WEventCardUpdate { + WEventCardAttacked(MTGCardInstance * card); + virtual Targetable * getTarget(int target); +}; + +//event when card attacks but is not blocked. +struct WEventCardAttackedNotBlocked : public WEventCardUpdate { + WEventCardAttackedNotBlocked(MTGCardInstance * card); + virtual Targetable * getTarget(int target); +}; + +//event when card attacks but is blocked. +struct WEventCardAttackedBlocked : public WEventCardUpdate { + WEventCardAttackedBlocked(MTGCardInstance * card); + virtual Targetable * getTarget(int target); +}; + +//event when card blocked. +struct WEventCardBlocked : public WEventCardUpdate { + WEventCardBlocked(MTGCardInstance * card); + virtual Targetable * getTarget(int target); +}; + +//event when card is sacrificed. +struct WEventCardSacrifice : public WEventCardUpdate { + WEventCardSacrifice(MTGCardInstance * card); + virtual Targetable * getTarget(int target); +}; + +//event when card is discarded. +struct WEventCardDiscard : public WEventCardUpdate { + WEventCardDiscard(MTGCardInstance * card); + virtual Targetable * getTarget(int target); +}; + //Event when a card's "defenser" status changes //before : attacker that card was blocking previously //after: attacker that card is blocking now @@ -108,6 +152,13 @@ struct WEventCreatureBlocker : public WEventCardUpdate { struct WEventBlockersChosen : public WEvent { }; +struct WEventcardDraw : public WEvent { + WEventcardDraw(Player * player,int nb_cards); + Player * player; + int nb_cards; + virtual Targetable * getTarget(Player * player); +}; + //Event when a blocker is reordered //exchangeWith: exchange card's position with exchangeWith's position //attacker:both card and exchangeWith *should* be in attacker's "blockers" list. diff --git a/projects/mtg/src/ActionLayer.cpp b/projects/mtg/src/ActionLayer.cpp index b941de2d2..1bc8d7728 100644 --- a/projects/mtg/src/ActionLayer.cpp +++ b/projects/mtg/src/ActionLayer.cpp @@ -101,8 +101,7 @@ void ActionLayer::Update(float dt){ cantCancel = 0; cancelCurrentAction(); } - } - + } } void ActionLayer::Render (){ @@ -120,7 +119,7 @@ void ActionLayer::Render (){ void ActionLayer::setCurrentWaitingAction(ActionElement * ae){ - assert(!ae || !currentWaitingAction); + assert(!ae || !currentWaitingAction);//this assert causes crashes when may abilities overlap each other on ai. this conidiation is preexsiting. currentWaitingAction = ae; if (!ae) cantCancel = 0; } diff --git a/projects/mtg/src/ActionStack.cpp b/projects/mtg/src/ActionStack.cpp index c3662f1ab..c3205945d 100644 --- a/projects/mtg/src/ActionStack.cpp +++ b/projects/mtg/src/ActionStack.cpp @@ -215,7 +215,6 @@ bool Spell::RetraceWasPaid(){ const string Spell::getDisplayName() const { return source->getName(); } - Spell::~Spell(){ SAFE_DELETE(cost); SAFE_DELETE(tc); @@ -390,7 +389,10 @@ int ActionStack::addAbility(MTGAbility * ability){ int ActionStack::addDraw(Player * player, int nb_cards){ DrawAction * draw = NEW DrawAction(mCount,player, nb_cards); - addAction(draw); + addAction(draw); + GameObserver *g = GameObserver::GetInstance(); + WEvent * e = NEW WEventcardDraw(player,nb_cards); + g->receiveEvent(e); return 1; } @@ -405,11 +407,19 @@ int ActionStack::AddNextGamePhase(){ NextGamePhase * next = NEW NextGamePhase(mCount); addAction(next); int playerId = 0; - if (game->currentActionPlayer == game->players[1]) playerId = 1; + game->currentActionPlayer = game->GetInstance()->currentActionPlayer; + if (game->currentActionPlayer == game->players[1]){ playerId = 1;} interruptDecision[playerId] = 1; return 1; } +int ActionStack::AddNextCombatStep(){ + if (getNext(NULL,NOT_RESOLVED)) return 0; + NextGamePhase * next = NEW NextGamePhase(mCount); + addAction(next); + return 1; +} + int ActionStack::setIsInterrupting(Player * player){ if (player == game->players[0]){ interruptDecision[0] = -1; diff --git a/projects/mtg/src/CardDescriptor.cpp b/projects/mtg/src/CardDescriptor.cpp index e5e4be303..d61fd42c6 100644 --- a/projects/mtg/src/CardDescriptor.cpp +++ b/projects/mtg/src/CardDescriptor.cpp @@ -34,6 +34,10 @@ void CardDescriptor::unsecureSetTapped(int i){ tapped = i; } +void CardDescriptor::unsecuresetfresh(int k){ + fresh = k; +} + void CardDescriptor::setNegativeSubtype( string value){ int id = Subtypes::subtypesList->find(value); addType(-id); @@ -150,15 +154,17 @@ MTGCardInstance * CardDescriptor::match(MTGCardInstance * card){ } } - if ((tapped == -1 && card->isTapped()) || (tapped == 1 && !card->isTapped())){ match = NULL; } + if ((fresh == -1 && card->fresh) || (fresh == 1 && !card->fresh)){ + match = NULL; + } + if ((isToken== -1 && card->isToken) || (isToken == 1 && !card->isToken)){ match = NULL; } - if (attacker == 1){ if (defenser == &AnyCard){ if (!card->attacker && !card->defenser) match = NULL; diff --git a/projects/mtg/src/CardGui.cpp b/projects/mtg/src/CardGui.cpp index d50726e99..c589cbee8 100644 --- a/projects/mtg/src/CardGui.cpp +++ b/projects/mtg/src/CardGui.cpp @@ -114,6 +114,7 @@ void CardGui::Render() else if (card->hasSubtype("island")) icon = resources.GetQuad("c_blue"); + if (icon){ icon->SetColor(ARGB(static_cast(actA),255,255,255)); renderer->RenderQuad(icon, actX, actY, 0); @@ -125,7 +126,7 @@ void CardGui::Render() if (card->isCreature()){ mFont->SetScale(DEFAULT_MAIN_FONT_SCALE); char buffer[200]; - sprintf(buffer, "%i/%i",card->power,card->life); + sprintf(buffer, "%i/%i",card->power,card->life); renderer->FillRect(actX - (12*actZ) , actY + 6* actZ, 25*actZ, 12*actZ, ARGB(((static_cast(actA))/2),0,0,0)); mFont->SetColor(ARGB(static_cast(actA),255,255,255)); mFont->SetScale(actZ); @@ -588,7 +589,7 @@ void CardGui::RenderBig(MTGCard* card, const Pos& pos){ if (quad->mHeight < quad->mWidth) { return tinyCropRender(card, pos, quad); } - quad->SetColor(ARGB((int)pos.actA,255,255,255)); + quad->SetColor(ARGB(255,255,255,255)); float scale = pos.actZ * 257.f / quad->mHeight; renderer->RenderQuad(quad, x, pos.actY, pos.actT, scale, scale); return; @@ -599,7 +600,7 @@ void CardGui::RenderBig(MTGCard* card, const Pos& pos){ if ((q = resources.RetrieveCard(card,CACHE_THUMB))) { float scale = pos.actZ * 250 / q->mHeight; - q->SetColor(ARGB((int)pos.actA,255,255,255)); + q->SetColor(ARGB(255,255,255,255)); renderer->RenderQuad(q, x, pos.actY, pos.actT, scale, scale); return; } diff --git a/projects/mtg/src/Damage.cpp b/projects/mtg/src/Damage.cpp index 9fc6bc3c6..00705ff35 100644 --- a/projects/mtg/src/Damage.cpp +++ b/projects/mtg/src/Damage.cpp @@ -32,7 +32,6 @@ int Damage::resolve(){ state = RESOLVED_OK; GameObserver * g = GameObserver::GetInstance(); WEvent * e = NEW WEventDamage(this); - //Replacement Effects e = g->replacementEffects->replace(e); if (!e) return 0; @@ -114,7 +113,6 @@ int Damage::resolve(){ // Poison on player Player * _target = (Player *)target; _target->poisonCount += damage;//this will be changed to poison counters. - } else if (target->type_as_damageable == DAMAGEABLE_PLAYER && ( source->has(Constants::POISONTOXIC) || source->has(Constants::POISONTWOTOXIC) || source->has(Constants::POISONTHREETOXIC) )) { //Damage + 1, 2, or 3 poison counters on player @@ -188,11 +186,10 @@ int DamageStack::resolve(){ for (int i = mCount-1; i>= 0; i--){ Damage * damage = (Damage*)mObjects[i]; if (damage->state == NOT_RESOLVED) damage->resolve(); - } - + } GameObserver::GetInstance()->receiveEvent(NEW WEventDamageStackResolved()); return 1; -} + } int DamageStack::receiveEvent(WEvent * e) { WEventDamageStackResolved *event = dynamic_cast(e); diff --git a/projects/mtg/src/DamagerDamaged.cpp b/projects/mtg/src/DamagerDamaged.cpp index 3a768dde3..df7260c20 100644 --- a/projects/mtg/src/DamagerDamaged.cpp +++ b/projects/mtg/src/DamagerDamaged.cpp @@ -57,6 +57,7 @@ void DamagerDamaged::Render(CombatStep mode) switch (mode) { case BLOCKERS : + case TRIGGERS : case ORDER : mFont->SetColor(ARGB(92,255,255,255)); break; diff --git a/projects/mtg/src/DuelLayers.cpp b/projects/mtg/src/DuelLayers.cpp index 95a0ecd29..88d0e0829 100644 --- a/projects/mtg/src/DuelLayers.cpp +++ b/projects/mtg/src/DuelLayers.cpp @@ -27,6 +27,7 @@ void DuelLayers::init(){ action->Add(NEW MTGRetraceRule(-1)); action->Add(NEW MTGAttackRule(-1)); action->Add(NEW MTGBlockRule(-1)); + action->Add(NEW MTGCombatTriggersRule(-1)); action->Add(NEW MTGLegendRule(-1)); action->Add(NEW MTGTokensCleanup(-1)); // needs to be before persist action->Add(NEW MTGPersistRule(-1)); diff --git a/projects/mtg/src/ExtraCost.cpp b/projects/mtg/src/ExtraCost.cpp index beff0cc5f..bdd6b619f 100644 --- a/projects/mtg/src/ExtraCost.cpp +++ b/projects/mtg/src/ExtraCost.cpp @@ -23,8 +23,7 @@ int ExtraCost::setSource(MTGCardInstance * _source){ if (tc) { tc->source = _source; - // Cryptic comment from cloned/refactored code: - // "Tapping targets is not targetting, protections do not apply" + // "extra cost is not targetting, protections do not apply" this is not cryptic at all :) make an ability you will understand it then. this keeps the target chooser from being unable to select a creature with shroud/protections. tc->targetter = NULL; } else @@ -93,19 +92,46 @@ int DiscardRandomCost::canPay(){ MTGGameZone * z = target->controller()->game->hand; int nbcards = z->nb_cards; if(nbcards < 1) return 0; + if(nbcards == 1 && z->hasCard(source)) return 0; return 1; } int DiscardRandomCost::doPay(){ MTGCardInstance * _target = (MTGCardInstance *) target; if(target){ - _target->controller()->game->discardRandom(_target->controller()->game->hand); + _target->controller()->game->discardRandom(_target->controller()->game->hand,source); target = NULL; if (tc) tc->initTargets(); return 1; } return 0; } +//a choosen discard + +DiscardCost * DiscardCost::clone() const{ + DiscardCost * ec = NEW DiscardCost(*this); + if (tc) ec->tc = tc->clone(); + return ec; +} + +DiscardCost::DiscardCost(TargetChooser *_tc) + : ExtraCost("Choose card to Discard", _tc){ +} + +int DiscardCost::doPay(){ + MTGCardInstance * _target = (MTGCardInstance *) target; + if(target){ + WEvent * e = NEW WEventCardDiscard(target); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); + _target->controller()->game->putInGraveyard(_target); + target = NULL; + if (tc) tc->initTargets(); + return 1; + } + return 0; +} +//to library cost ToLibraryCost * ToLibraryCost::clone() const{ ToLibraryCost * ec = NEW ToLibraryCost(*this); @@ -242,6 +268,39 @@ int BounceTargetCost::doPay(){ return 0; } +//Bounce as cost for ninja +Ninja * Ninja::clone() const{ + Ninja * ec = NEW Ninja(*this); + if (tc) ec->tc = tc->clone(); + return ec; +} + +Ninja::Ninja(TargetChooser *_tc) + : ExtraCost("Select unblocked attacker", _tc){ +} + +int Ninja::isPaymentSet(){ + GameObserver * g = GameObserver::GetInstance(); + int currentPhase = g->getCurrentGamePhase(); + if (target && ((target->isAttacker() && target->blocked == true) || target->isAttacker() < 1 || currentPhase != Constants::MTG_PHASE_COMBATBLOCKERS)){ tc->removeTarget(target);target = NULL; return 0;} + if(target) return 1; + return 0; +} + +int Ninja::doPay(){ + MTGCardInstance * _target = (MTGCardInstance *) target; + if(target){ + target->controller()->game->putInHand(target); + target = NULL; + if (tc) tc->initTargets(); + return 1; + } + return 0; +} + +//endbouncetargetcostforninja +//------------------------------------------------------------ + SacrificeCost * SacrificeCost::clone() const{ SacrificeCost * ec = NEW SacrificeCost(*this); if (tc) ec->tc = tc->clone(); @@ -254,6 +313,9 @@ SacrificeCost::SacrificeCost(TargetChooser *_tc) int SacrificeCost::doPay(){ if(target){ + WEvent * e = NEW WEventCardSacrifice(target); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); target->controller()->game->putInGraveyard(target); target = NULL; if (tc) tc->initTargets(); diff --git a/projects/mtg/src/GameObserver.cpp b/projects/mtg/src/GameObserver.cpp index 6eb2b31d7..f3e7aa784 100644 --- a/projects/mtg/src/GameObserver.cpp +++ b/projects/mtg/src/GameObserver.cpp @@ -74,16 +74,17 @@ void GameObserver::nextGamePhase(){ if (FIRST_STRIKE == combatStep || END_FIRST_STRIKE == combatStep || DAMAGE == combatStep) { nextCombatStep(); return; } + if (cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS) - if (BLOCKERS == combatStep) { nextCombatStep(); return; } + if (BLOCKERS == combatStep || TRIGGERS == combatStep) { nextCombatStep(); return; } phaseRing->forward(); - + //Go directly to end of combat if no attackers if (cPhaseOld->id == Constants::MTG_PHASE_COMBATATTACKERS && !(currentPlayer->game->inPlay->getNextAttacker(NULL))){ phaseRing->forward(); phaseRing->forward(); - } + } Phase * cPhase = phaseRing->getCurrentPhase(); currentGamePhase = cPhase->id; @@ -151,8 +152,12 @@ void GameObserver::nextCombatStep() { switch (combatStep) { - case BLOCKERS : receiveEvent(NEW WEventBlockersChosen()); - receiveEvent(NEW WEventCombatStepChange(combatStep = ORDER)); return; + case BLOCKERS : + receiveEvent(NEW WEventBlockersChosen()); + receiveEvent(NEW WEventCombatStepChange(combatStep = TRIGGERS)); + return; + + case TRIGGERS : receiveEvent(NEW WEventCombatStepChange(combatStep = ORDER)); return; case ORDER : receiveEvent(NEW WEventCombatStepChange(combatStep = FIRST_STRIKE)); return; case FIRST_STRIKE : receiveEvent(NEW WEventCombatStepChange(combatStep = END_FIRST_STRIKE)); return; case END_FIRST_STRIKE : receiveEvent(NEW WEventCombatStepChange(combatStep = DAMAGE)); return; @@ -164,17 +169,27 @@ void GameObserver::nextCombatStep() void GameObserver::userRequestNextGamePhase(){ if (mLayers->stackLayer()->getNext(NULL,0,NOT_RESOLVED)) return; if (getCurrentTargetChooser()) return; + + bool executeNextPhaseImmediately = true; + Phase * cPhaseOld = phaseRing->getCurrentPhase(); - - if ((cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS && combatStep == ORDER) || + (cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS && combatStep == TRIGGERS)|| cPhaseOld->id == Constants::MTG_PHASE_COMBATDAMAGE || opponent()->isAI() || - options[Options::optionInterrupt(currentGamePhase)].number) - mLayers->stackLayer()->AddNextGamePhase(); - else - nextGamePhase(); + options[Options::optionInterrupt(currentGamePhase)].number) + { + executeNextPhaseImmediately = false; + } + if (executeNextPhaseImmediately) + { + nextGamePhase(); + } + else + { + mLayers->stackLayer()->AddNextGamePhase(); + } } int GameObserver::forceShuffleLibraries(){ @@ -280,15 +295,26 @@ void GameObserver::Update(float dt){ Player * player = currentPlayer; if (Constants::MTG_PHASE_COMBATBLOCKERS == currentGamePhase && BLOCKERS == combatStep) player = player->opponent(); + currentActionPlayer = player; if (isInterrupting) player = isInterrupting; mLayers->Update(dt,player); + while (mLayers->actionLayer()->stuffHappened){ mLayers->actionLayer()->Update(0); } stateEffects(); oldGamePhase = currentGamePhase; + + if (combatStep == TRIGGERS) + { + if (!mLayers->stackLayer()->getNext(NULL,0,NOT_RESOLVED)) + { + mLayers->stackLayer()->AddNextCombatStep(); + } + } + //Auto skip Phases int skipLevel = (player->playMode == Player::MODE_TEST_SUITE) ? Constants::ASKIP_NONE : options[Options::ASPHASES].number; @@ -331,7 +357,7 @@ void GameObserver::stateEffects() card->afterDamage(); //Remove auras that don't have a valid target anymore - if (card->target && !isInPlay(card->target) && !card->hasType("equipment")){ + if (card->target && !isInPlay(card->target) && !card->hasType("equipment")){ players[i]->game->putInGraveyard(card); } } diff --git a/projects/mtg/src/GuiCombat.cpp b/projects/mtg/src/GuiCombat.cpp index 2e4e9e795..91acb671b 100644 --- a/projects/mtg/src/GuiCombat.cpp +++ b/projects/mtg/src/GuiCombat.cpp @@ -133,7 +133,10 @@ bool GuiCombat::clickOK(){ cursor_pos = NONE; switch (step) { - case BLOCKERS : assert(false); return false; // that should not happen + case BLOCKERS : + case TRIGGERS : + assert(false); return false; // that should not happen + case ORDER : go->nextGamePhase(); return true; case FIRST_STRIKE : return false; case DAMAGE : validateDamage(); return true; @@ -441,9 +444,14 @@ int GuiCombat::receiveEventMinus(WEvent* e) else if (WEventCombatStepChange* event = dynamic_cast(e)) switch (event->step) { - case BLOCKERS: - break; - case ORDER: + case BLOCKERS: + break; + + case TRIGGERS: + step = TRIGGERS; + return 1; + + case ORDER: { if (ORDER == step) return 0; // Why do I take this twice ? >.> if (!go->currentPlayer->displayStack()) { go->nextCombatStep(); return 1; } diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index f6f8a00ba..f5355a070 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -153,7 +153,133 @@ TriggeredAbility * AbilityFactory::parseTrigger(string magicText, int id, Spell TargetChooser *tc = tcf.createTargetChooser(starget,card); tc->targetter = NULL; - return NEW TrCardTapped(id,card,tc); + return NEW TrCardTapped(id,card,tc,true); + } + + //Card unTapped + found = s.find("untapping("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+10,end - found - 10); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + + return NEW TrCardTapped(id,card,tc,false); + } + + //Card Tapped for mana + found = s.find("tappedformana("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+14,end - found - 14); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + + return NEW TrCardTappedformana(id,card,tc,true); + } + + //Card is attacking + found = s.find("attacking("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+10,end - found - 10); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + + return NEW TrCardAttacked(id,card,tc); + } + + //Card card attacked and is not blocked + found = s.find("notblocked("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+11,end - found - 11); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + + return NEW TrCardAttackedNotBlocked(id,card,tc); + } + + //Card card attacked and is blocked + found = s.find("blocked("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+8,end - found - 8); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + found = s.find("from("); + + TargetChooser *fromTc = NULL; + if (found != string::npos){ + end = s.find (")", found); + starget = s.substr(found+5,end - found - 5); + fromTc = tcf.createTargetChooser(starget,card); + fromTc->targetter = NULL; + } + + return NEW TrCardAttackedBlocked(id,card,tc,fromTc); + } + + //Card card is a blocker + found = s.find("blocking("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+9,end - found - 9); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + found = s.find("from("); + + TargetChooser *fromTc = NULL; + if (found != string::npos){ + end = s.find (")", found); + starget = s.substr(found+5,end - found - 5); + fromTc = tcf.createTargetChooser(starget,card); + fromTc->targetter = NULL; + } + + return NEW TrCardBlocked(id,card,tc,fromTc); + } + + //Card card is drawn + found = s.find("drawn("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+6,end - found - 6); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + + return NEW TrcardDrawn(id,card,tc); + } + + //Card is sacrificed + found = s.find("sacrificed("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+11,end - found - 11); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + + return NEW TrCardSacrificed(id,card,tc); + } + + //Card is sacrificed + found = s.find("discarded("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+10,end - found - 10); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + + return NEW TrCardDiscarded(id,card,tc); } //Card Damaging @@ -173,7 +299,47 @@ TriggeredAbility * AbilityFactory::parseTrigger(string magicText, int id, Spell fromTc = tcf.createTargetChooser(starget,card); fromTc->targetter = NULL; } - return NEW TrDamaged(id,card,tc,fromTc); + return NEW TrDamaged(id,card,tc,fromTc, 0); + } + + //Card Damaging combat + found = s.find("combatdamage("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+13,end - found - 13); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + found = s.find("from("); + + TargetChooser *fromTc = NULL; + if (found != string::npos){ + end = s.find (")", found); + starget = s.substr(found+5,end - found - 5); + fromTc = tcf.createTargetChooser(starget,card); + fromTc->targetter = NULL; + } + return NEW TrDamaged(id,card,tc,fromTc, 1); + } + + //Card Damaging non combat + found = s.find("damagenoncombat("); + if (found != string::npos){ + size_t end = s.find (")"); + string starget = s.substr(found+16,end - found - 16); + TargetChooserFactory tcf; + TargetChooser *tc = tcf.createTargetChooser(starget,card); + tc->targetter = NULL; + found = s.find("from("); + + TargetChooser *fromTc = NULL; + if (found != string::npos){ + end = s.find (")", found); + starget = s.substr(found+5,end - found - 5); + fromTc = tcf.createTargetChooser(starget,card); + fromTc->targetter = NULL; + } + return NEW TrDamaged(id,card,tc,fromTc, 2); } int who = 0; @@ -320,8 +486,6 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG limit = atoi(sWithoutTc.substr(limit_str+6).c_str()); } - - AEquip *ae = dynamic_cast(a); if (ae){ ae->cost = cost; @@ -331,7 +495,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG } ae->tc = tc; return ae; - } + } if (tc) return NEW GenericTargetAbility(id, card, tc, a,cost, doTap,limit,restrictions,dest); return NEW GenericActivatedAbility(id, card, a,cost,doTap,limit,restrictions,dest); @@ -339,7 +503,6 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG SAFE_DELETE(cost); } - //kicker cost found = s.find("kicker "); if (found == 0){ @@ -492,7 +655,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG case 1: result = NEW AThisForEach(id, card, _target, td, a); break; default: result = NULL; } - if (result) result->oneShot = oneShot; + if (result){ result->oneShot = oneShot;} return result; } return NULL; @@ -633,6 +796,14 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return a; } + //ninjutsu + found = s.find("ninjutsu"); + if (found != string::npos){ + MTGAbility * a = NEW ANinja(id,card,target); + a->oneShot = 1; + return a; + } + //Fizzle (counterspell...) found = s.find("fizzle"); if (found != string::npos){ @@ -660,10 +831,17 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG if (star != string::npos) multiplier = NEW WParsedInt(s.substr(star+1),spell,card); size_t end = s.find(")", found); int tokenId = atoi(s.substr(found + 6,end - found - 6).c_str()); + int who; + size_t opponent = s.find("opponent"); + if (opponent != string::npos){ + who = 1; + }else{ + who = 0; + } if (tokenId){ MTGCard * safetycard = GameApp::collection->getCardById(tokenId); if (safetycard){//contenue - ATokenCreator * tok = NEW ATokenCreator(id,card,NULL,tokenId,0, multiplier); + ATokenCreator * tok = NEW ATokenCreator(id,card,NULL,tokenId,0, multiplier,who); tok->oneShot = 1; return tok; }else{ @@ -687,8 +865,14 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG if(!spt.find("XX/XX") || !spt.find("xx/xx")){value = spell->computeXX(card);} parsePowerToughness(spt,&power, &toughness); string sabilities = s.substr(end+1); - - ATokenCreator * tok = NEW ATokenCreator(id,card,NULL,sname,stypes,power + value,toughness + value,sabilities,0, multiplier); + if(s.find("opponent")){ + if (opponent != string::npos){ + who = 1; + }else{ + who = 0; + } + } + ATokenCreator * tok = NEW ATokenCreator(id,card,NULL,sname,stypes,power + value,toughness + value,sabilities,0, multiplier,who); tok->oneShot = 1; return tok; } @@ -743,6 +927,25 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return a; } + //clone + found = s.find("clone"); + if (found != string::npos){ + int who; + who = 0; + found = s.find("opponent"); + if (found != string::npos){ + who = 1; + } + string with; + found = s.find("with("); + if (found != string::npos){ + size_t end = s.find (")", found); + with = s.substr(found+5,end - found - 5); + } + MTGAbility * a = NEW AACloner(id,card,target,0,who,with); + a->oneShot = 1; + return a; + } //Bury, destroy string destroys[] = {"bury","destroy"}; @@ -757,11 +960,26 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG } } + //sacrifices and discards + string sacdis[] = {"sacrifice","reject"}; + int sacdisTypes[]= {1, 0}; + for (int i = 0; i < 2; ++i){ + found = s.find(sacdis[i]); + if (found != string::npos){ + int sacrifice = sacdisTypes[i]; + MTGAbility * a = NEW AASacDis(id,card,target,sacrifice); + a->oneShot = 1; + return a; + } + } + + int who = TargetChooser::UNSET; if (s.find(" controller") != string::npos) who=TargetChooser::CONTROLLER; if (s.find(" opponent") != string::npos) who=TargetChooser::OPPONENT; if (s.find(" targetcontroller") != string::npos) who=TargetChooser::TARGET_CONTROLLER; + if (s.find(" owner") != string::npos) who=TargetChooser::OWNER; found = s.find("ueot"); if (found!= string::npos) forceUEOT = 1; @@ -798,6 +1016,53 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG } return ab; } + //PreventCombat Damage + found = s.find("preventalldamage"); + if (found != string::npos){ + string to = ""; + string from = ""; + found = s.find("to("); + if (found != string::npos){ + size_t end = s.find (")", found); + to = s.substr(found+3,end - found - 3); + } + found = s.find("from("); + if (found != string::npos){ + size_t end = s.find (")", found); + from = s.substr(found+5,end - found - 5); + } + MTGAbility * ab; + if (forceUEOT){ + ab = NEW APreventAllCombatDamageUEOT(id,card,to,from,1); + }else{ + ab = NEW APreventAllCombatDamage(id,card,to,from,1); + } + return ab; + } + + //PreventCombat Damage + found = s.find("preventallnoncombat"); + if (found != string::npos){ + string to = ""; + string from = ""; + found = s.find("to("); + if (found != string::npos){ + size_t end = s.find (")", found); + to = s.substr(found+3,end - found - 3); + } + found = s.find("from("); + if (found != string::npos){ + size_t end = s.find (")", found); + from = s.substr(found+5,end - found - 5); + } + MTGAbility * ab; + if (forceUEOT){ + ab = NEW APreventAllCombatDamageUEOT(id,card,to,from,2); + }else{ + ab = NEW APreventAllCombatDamage(id,card,to,from,2); + } + return ab; + } //PreventCombat Damage found = s.find("fog"); @@ -838,6 +1103,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG a->oneShot = 1; return a; } + //remove poison found = s.find("removepoison:"); if (found != string::npos){ @@ -1048,11 +1314,29 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG if (parsePowerToughness(spt,&power, &toughness)){ int MaxOpponent = atoi(s.substr(end+1,end+2).c_str()); return NEW ARampageAbility(id,card,power,toughness,MaxOpponent); + } + return NULL; + } +// the following is NOT dead code. this is for stacking flanking. except auras do not yet support + //giving creatures activated/MTGAbility keywords. soon as i add this support this will be uncommented for stacking flanking. + // //flanking + // found = s.find("flanking"); + // if (found != string::npos){ + // return NEW AFlankerAbility(id,card); + //} + + //bushido + found = s.find("bushido("); + if (found != string::npos){ + int end = s.find(")", found); + string spt = s.substr(8,end - 1); + int power, toughness; + if (parsePowerToughness(spt,&power, &toughness)){ + return NEW ABushidoAbility(id,card,power,toughness); } return NULL; } - //counter found = s.find("counter("); if (found != string::npos){ @@ -1289,6 +1573,14 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return a; } + //switch targest power with toughness + found = s.find("twist"); + if (found != string::npos){ + MTGAbility * a = NEW ATwistUEOT(id,card,target); + a->oneShot = 1; + return a; + } + //Untapper (Ley Druid...) found = s.find("untap"); if (found != string::npos){ @@ -1331,7 +1623,10 @@ int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, Targ if (AAsLongAs * abi = dynamic_cast(a)) return abilityEfficiency(abi->ability,p, mode,tc); if (AForeach * abi = dynamic_cast(a)) return abilityEfficiency(abi->ability,p, mode,tc); if (dynamic_cast(a)) return BAKA_EFFECT_BAD; - if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; + if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; + if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; + if (dynamic_cast(a)) return BAKA_EFFECT_BAD; + if (dynamic_cast(a)) return BAKA_EFFECT_GOOD; if (dynamic_cast(a)) return BAKA_EFFECT_BAD; if (AACounter * ac = dynamic_cast(a)) { bool negative_effect = ac->power < 0 || ac->toughness < 0; @@ -1805,7 +2100,7 @@ void AbilityFactory::addAbilities(int _id, Spell * spell){ { int xCost = computeX(spell,card); for (int i = 0; i < xCost; i++){ - game->opponent()->game->discardRandom(game->opponent()->game->hand); + game->opponent()->game->discardRandom(game->opponent()->game->hand,card); } break; } @@ -2076,6 +2371,10 @@ void AbilityFactory::addAbilities(int _id, Spell * spell){ game->addObserver(NEW AExalted(_id, card)); } + if (card->basicAbilities[Constants::FLANKING]){ + game->addObserver(NEW AFlankerAbility(_id, card)); + } + // Tested works the first r10 did not function because of the mistake in the array of the definition if (card->basicAbilities[Constants::FORESTHOME]){ game->addObserver(NEW AStrongLandLinkCreature(_id, card, "forest")); @@ -2715,7 +3014,10 @@ AManaProducer::AManaProducer(int id, MTGCardInstance * card, Targetable * t, Man int result = 0; if (!mana) mana = game->currentlyActing()->getManaPool(); if (_card == source && (!tap || !source->isTapped()) && game->currentlyActing()->game->inPlay->hasCard(source) && (source->hasType(Subtypes::TYPE_LAND) || !tap || !source->hasSummoningSickness()) ){ - if (!cost || mana->canAfford(cost)) result = 1; + if (!cost || mana->canAfford(cost)) + { + result = 1; + } } return result; } @@ -2746,7 +3048,12 @@ AManaProducer::AManaProducer(int id, MTGCardInstance * card, Targetable * t, Man GameObserver::GetInstance()->currentlyActing()->getManaPool()->pay(cost); cost->doPayExtra(); } - if (tap) source->tap(); + if (tap){ + GameObserver *g = GameObserver::GetInstance(); + WEvent * e = NEW WEventCardTappedForMana(source, 0, 1); + g->receiveEvent(e); + source->tap(); + } if (options[Options::SFXVOLUME].number > 0){ JSample * sample = resources.RetrieveSample("mana.wav"); @@ -2833,6 +3140,8 @@ AManaProducer::AManaProducer(int id, MTGCardInstance * card, Targetable * t, Man return source->controller(); case TargetChooser::OPPONENT: return source->controller()->opponent(); + case TargetChooser::OWNER: + return source->owner; default: return target; } diff --git a/projects/mtg/src/MTGCardInstance.cpp b/projects/mtg/src/MTGCardInstance.cpp index 4c5f2fae7..a3bbe8cbd 100644 --- a/projects/mtg/src/MTGCardInstance.cpp +++ b/projects/mtg/src/MTGCardInstance.cpp @@ -35,6 +35,7 @@ MTGCardInstance::MTGCardInstance(MTGCard * card, MTGPlayerCards * arg_belongs_to banding = NULL; life = toughness; preventable = 0; + flanked = 0; } void MTGCardInstance::copy(MTGCardInstance * card){ @@ -99,6 +100,11 @@ void MTGCardInstance::initMTGCI(){ tapped = 0; untapping = 0; frozen = 0; + fresh = 0; + didattacked = 0; + didblocked = 0; + notblocked = 0; + sunburst = NULL; equipment = NULL; boughtback = 0; flashedback = 0; @@ -106,6 +112,7 @@ void MTGCardInstance::initMTGCI(){ reduxamount = 0; summoningSickness = 1; preventable = 0; + flanked = 0; target = NULL; type_as_damageable = DAMAGEABLE_MTGCARDINSTANCE; banding = NULL; @@ -184,6 +191,10 @@ int MTGCardInstance::afterDamage(){ int MTGCardInstance::bury(){ Player * p = controller(); + if (basicAbilities[Constants::EXILEBURY]){ + p->game->putInZone(this,p->game->inPlay,owner->game->exile); + return 1; + } if (!basicAbilities[Constants::INDESTRUCTIBLE]){ p->game->putInZone(this,p->game->inPlay,owner->game->graveyard); return 1; @@ -203,6 +214,40 @@ int MTGCardInstance::has(int basicAbility){ return basicAbilities[basicAbility]; } + +//sets card as attacked and sends events +void MTGCardInstance::eventattacked(){ + didattacked = 1; + WEvent * e = NEW WEventCardAttacked(this); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); +} + +//sets card as attacked and sends events +void MTGCardInstance::eventattackednotblocked(){ + didattacked = 1; + WEvent * e = NEW WEventCardAttackedNotBlocked(this); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); +} + +//sets card as attacked and sends events +void MTGCardInstance::eventattackedblocked(){ + didattacked = 1; + WEvent * e = NEW WEventCardAttackedBlocked(this); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); +} + +//sets card as blocking and sends events +void MTGCardInstance::eventblocked(){ + didblocked = 1; + WEvent * e = NEW WEventCardBlocked(this); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); +} + + //Taps the card void MTGCardInstance::tap(){ if (tapped) return; @@ -265,6 +310,8 @@ int MTGCardInstance::initAttackersDefensers(){ banding = NULL; blockers.clear(); blocked = false; + didattacked = 0; + didblocked = 0; return 1; } @@ -273,7 +320,10 @@ int MTGCardInstance::cleanup(){ initAttackersDefensers(); life=toughness; GameObserver * game = GameObserver::GetInstance(); - if (!game || game->currentPlayer == controller()) summoningSickness = 0; + if (!game || game->currentPlayer == controller()) + { + summoningSickness = 0; + } if (previous && !previous->stillInUse()){ SAFE_DELETE(previous); } @@ -471,7 +521,9 @@ int MTGCardInstance::getDefenserRank(MTGCardInstance * blocker){ int MTGCardInstance::removeBlocker(MTGCardInstance * blocker){ blockers.remove(blocker); - if (!blockers.size()) blocked = false; + if (!blockers.size()){ + blocked = false; + } return 1; } @@ -537,13 +589,18 @@ int MTGCardInstance::toggleDefenser(MTGCardInstance * opponent){ if (canBlock()){ if (canBlock(opponent)){ setDefenser(opponent); + didblocked = 1; + if(opponent && opponent->controller()->isAI()){ + opponent->view->actZ += .8; + opponent->view->actT -= .2; + } + if(!opponent) didblocked = 0; return 1; - } - } + } + } return 0; } - int MTGCardInstance::addProtection(TargetChooser * tc){ tc->targetter = NULL; protections.push_back(tc); diff --git a/projects/mtg/src/MTGDeck.cpp b/projects/mtg/src/MTGDeck.cpp index a1e626a5e..620656926 100644 --- a/projects/mtg/src/MTGDeck.cpp +++ b/projects/mtg/src/MTGDeck.cpp @@ -96,6 +96,11 @@ int MTGAllCards::processConfLine(string &s, MTGCard *card, CardPrimitive * primi colors.push_back(j); } } + if(colors.size()) + { + primitive->setColor(0,1); + primitive->removeColor(0); + } list::iterator it; for ( it=colors.begin() ; it != colors.end(); it++ ){ primitive->setColor(*it); diff --git a/projects/mtg/src/MTGDefinitions.cpp b/projects/mtg/src/MTGDefinitions.cpp index cb1dd2d84..10cec9096 100644 --- a/projects/mtg/src/MTGDefinitions.cpp +++ b/projects/mtg/src/MTGDefinitions.cpp @@ -89,6 +89,9 @@ const char* Constants::MTGBasicAbilities[] = { "leyline", "playershroud", "controllershroud", +"sunburst", +"flanking", +"exiledeath", }; diff --git a/projects/mtg/src/MTGGameZones.cpp b/projects/mtg/src/MTGGameZones.cpp index 0123a46e2..9db1c9fa0 100644 --- a/projects/mtg/src/MTGGameZones.cpp +++ b/projects/mtg/src/MTGGameZones.cpp @@ -249,10 +249,13 @@ MTGCardInstance * MTGPlayerCards::putInZone(MTGCardInstance * card, MTGGameZone return card; //Error } -void MTGPlayerCards::discardRandom(MTGGameZone * from){ +void MTGPlayerCards::discardRandom(MTGGameZone * from,MTGCardInstance * source){ if (!from->nb_cards) return; int r = WRand() % (from->nb_cards); + WEvent * e = NEW WEventCardDiscard(from->cards[r]); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); putInZone(from->cards[r],from, graveyard); } diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index 984f42860..87a718976 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -49,6 +49,23 @@ int MTGPutInPlayRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana) //cost of card. if (playerMana->canAfford(cost)){ +//------- + if(card->has(Constants::SUNBURST)){ + for(int i = 1; i != 6;i++) + { + if(player->getManaPool()->hasColor(i)){ + if(card->getManaCost()->hasColor(i) > 0){//do nothing if the card already has this color. + }else{ + if(card->sunburst < card->getManaCost()->getConvertedCost()){ + card->getManaCost()->add(i,1); + card->getManaCost()->remove(0,1); + card->sunburst += 1; + } + } + } +//------- + } + } return 1;//play if you can afford too. } } @@ -736,6 +753,83 @@ MTGAttackRule * MTGAttackRule::clone() const{ return a; } +//this rules handles returning cards to combat triggers for activations. +MTGCombatTriggersRule::MTGCombatTriggersRule(int _id):MTGAbility(_id,NULL){ + aType=MTGAbility::MTG_COMBATTRIGGERS_RULE; +} + +int MTGCombatTriggersRule::receiveEvent(WEvent *e){ + if (WEventPhaseChange* event = dynamic_cast(e)) { + if (Constants::MTG_PHASE_COMBATATTACKERS == event->from->id) { + Player * p = game->currentPlayer; + MTGGameZone * z = p->game->inPlay; + for (int i= 0; i < z->nb_cards; i++){ + MTGCardInstance * card = z->cards[i]; + if (card && card->isAttacker()){ + card->eventattacked(); + } + } + } + if (Constants::MTG_PHASE_COMBATEND == event->from->id) { + Player * p = game->currentPlayer->opponent(); + MTGGameZone * z = p->game->inPlay; + for (int i= 0; i < z->nb_cards; i++){ + MTGCardInstance * card = z->cards[i]; + if (card){ + card->didattacked = 0; + card->didblocked = 0; + card->notblocked = 0; + } + } + } +//--------------- + } + if (WEventBlockersChosen * event = dynamic_cast(e)) + { + Player * p = game->currentPlayer; + MTGGameZone * z = p->game->inPlay; + for (int i= 0; i < z->nb_cards; i++){ + MTGCardInstance * card = z->cards[i]; + if (card && card->isAttacker() && !card->blocked) + { + card->eventattackednotblocked(); + card->notblocked += 1; + } + if (card && card->isAttacker() && card->blocked) + { + card->eventattackedblocked(); + } + } + + MTGGameZone* opponentZone = game->currentPlayer->opponent()->game->inPlay; + for (int i = 0; i < opponentZone->nb_cards; i++) + { + MTGCardInstance* card = opponentZone->cards[i]; + if (card && card->didblocked > 0){ + card->eventblocked(); + } + } + } + return 0; +} + +//trigger rules are never distroyed +int MTGCombatTriggersRule::testDestroy(){ + return 0; +} + +ostream& MTGCombatTriggersRule::toString(ostream& out) const +{ + out << "MTGCombatTriggersRule ::: ("; + return MTGAbility::toString(out) << ")"; +} + +MTGCombatTriggersRule * MTGCombatTriggersRule::clone() const{ + MTGCombatTriggersRule * a = NEW MTGCombatTriggersRule(*this); + a->isClone = 1; + return a; +} +///------------ OtherAbilitiesEventReceiver::OtherAbilitiesEventReceiver(int _id):MTGAbility(_id,NULL){ } @@ -773,7 +867,7 @@ MTGBlockRule::MTGBlockRule(int _id):MTGAbility(_id,NULL){ int MTGBlockRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana){ if (currentPhase == Constants::MTG_PHASE_COMBATBLOCKERS && !game->isInterrupting && card->controller() == game->currentlyActing()){ - if (card->canBlock()) return 1; + if (card->canBlock())return 1; } return 0; } @@ -1085,6 +1179,12 @@ int MTGCantCasterstart::receiveEvent(WEvent * event){ WEventZoneChange * e = (WEventZoneChange *) event; MTGCardInstance * card = e->card->previous; if(card){ + if(e->from == e->card->controller()->game->battlefield && e->to == e->card->controller()->game->graveyard){ + e->card->fresh = 1; + } + if(e->to == e->card->controller()->game->battlefield){ + e->card->fresh = 1; + } if (card->basicAbilities[Constants::BOTHCANTCAST] || card->basicAbilities[Constants::BOTHNOCREATURE] || card->basicAbilities[Constants::CANTCAST] || card->basicAbilities[Constants::CANTCASTCREATURE] || card->basicAbilities[Constants::CANTCASTTWO] || card->basicAbilities[Constants::ONLYONEBOTH] || card->basicAbilities[Constants::NOMAXHAND] ){ int ok = 0; for (int i = 0; i < 2 ; i++){ @@ -1240,8 +1340,30 @@ int MTGSneakAttackRule::receiveEvent(WEvent *e){ MTGGameZone * z = p->game->inPlay; for (int i= 0; i < z->nb_cards; i++){ MTGCardInstance * card = z->cards[i]; - if (card->has(Constants::TREASON)) {p->game->putInGraveyard(card);i--;} + while(card->flanked){//undoes the flanking on a card + card->power += 1; + card->addToToughness(1); + card->flanked -= 1; + } + if (card->has(Constants::TREASON)) + { + WEvent * e = NEW WEventCardSacrifice(card); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); + p->game->putInGraveyard(card);i--; + } if (card->has(Constants::UNEARTH)) {p->game->putInExile(card);i--;} + if (card->fresh) card->fresh = 0; + if (card->has(Constants::ONLYONEBOTH)) + { + card->controller()->castcount = 0; + card->controller()->opponent()->castcount = 0; + } + } + MTGGameZone * f = p->game->graveyard; + for (int k= 0; k < f->nb_cards; k++){ + MTGCardInstance * card = f->cards[k]; + card->fresh = 0; } } } diff --git a/projects/mtg/src/ManaCost.cpp b/projects/mtg/src/ManaCost.cpp index 5e955e980..213106406 100644 --- a/projects/mtg/src/ManaCost.cpp +++ b/projects/mtg/src/ManaCost.cpp @@ -97,10 +97,19 @@ ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstan } break; case 'd': //DiscardRandom cost + if (value == "d"){ manaCost->addExtraCost(NEW DiscardRandomCost(tc)); + }else{ + manaCost->addExtraCost(NEW DiscardCost(tc)); + } break; case 'm': //Mill yourself as a cost manaCost->addExtraCost(NEW MillCost(tc)); + break; + case 'n': //return unblocked attacker cost + TargetChooserFactory tcf; + tc = tcf.createTargetChooser("creature|myBattlefield", c); + manaCost->addExtraCost(NEW Ninja(tc)); break; case 'c': //Counters { diff --git a/projects/mtg/src/TargetChooser.cpp b/projects/mtg/src/TargetChooser.cpp index 7b02184b8..7a350e666 100644 --- a/projects/mtg/src/TargetChooser.cpp +++ b/projects/mtg/src/TargetChooser.cpp @@ -24,6 +24,20 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta return NEW CardTargetChooser(target,card); }; + found = s.find("opponent"); + if (found == 0){ + int maxtargets = 1; + Player * opponent = card->controller()->opponent(); + return NEW PlayerTargetChooser(card,maxtargets,opponent); + }; + + found = s.find("controller"); + if (found == 0){ + int maxtargets = 1; + Player * controller = card->controller(); + return NEW PlayerTargetChooser(card,maxtargets,controller); + }; + found = s.find("other "); if (found == 0){ other = true; @@ -191,26 +205,35 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta }else{ cd->attacker = 1; } + } //Blocker - }else if (attribute.find("blocking") != string::npos){ + else if (attribute.find("blocking") != string::npos){ if (minus){ cd->defenser = & MTGCardInstance::NoCard; }else{ cd->defenser = & MTGCardInstance::AnyCard; } + } //Tapped, untapped - }else if (attribute.find("tapped") != string::npos){ + else if (attribute.find("tapped") != string::npos){ if (minus){ cd->unsecureSetTapped(-1); }else{ cd->unsecureSetTapped(1); } //Token - }else if (attribute.find("token") != string::npos){ + }else if (attribute.find("token") != string::npos){ if (minus){ cd->isToken = -1; }else{ cd->isToken = 1; + } + //put in its zone this turn + }else if (attribute.find("fresh") != string::npos){ + if (minus){ + cd->unsecuresetfresh(-1); + }else{ + cd->unsecuresetfresh(1); } //Power restrictions }else if (attribute.find("power") != string::npos){ @@ -520,7 +543,6 @@ bool TypeTargetChooser::canTarget(Targetable * target){ MTGCardInstance * card = (MTGCardInstance *) target; for (int i= 0; i < nbtypes; i++){ if (card->hasSubtype(types[i])) return true; - if (card->has(Constants::CHANGELING)) return true; if (Subtypes::subtypesList->find(card->getLCName()) == types[i]) return true; } return false; diff --git a/projects/mtg/src/ThisDescriptor.cpp b/projects/mtg/src/ThisDescriptor.cpp index 1c131f1d3..5d5aa8684 100644 --- a/projects/mtg/src/ThisDescriptor.cpp +++ b/projects/mtg/src/ThisDescriptor.cpp @@ -137,6 +137,28 @@ ThisDescriptor * ThisDescriptorFactory::createThisDescriptor(string s){ return NULL; } + //whenever this creature attacks do effect + found = s.find("attacking"); + if (found != string::npos) { + ThisAttacked * td = NEW ThisAttacked(criterion); + if (td) { + td->comparisonMode = mode; + return td; + } + return NULL; + } + + //whenever this creature attacks do effect + found = s.find("notblocked"); + if (found != string::npos) { + ThisNotBlocked * td = NEW ThisNotBlocked(criterion); + if (td) { + td->comparisonMode = mode; + return td; + } + return NULL; + } + //controller life found = s.find("opponentlife"); if (found != string::npos) { @@ -262,6 +284,25 @@ int ThisEquip::match(MTGCardInstance * card){ return matchValue(card->equipment); } +ThisAttacked::ThisAttacked(int attack){ + + comparisonCriterion = attack; +} + +int ThisAttacked::match(MTGCardInstance * card){ + + return matchValue(card->didattacked); +} + +ThisNotBlocked::ThisNotBlocked(int unblocked){ + + comparisonCriterion = unblocked; +} + +int ThisNotBlocked::match(MTGCardInstance * card){ + + return matchValue(card->notblocked); +} ThisToughness::ThisToughness(int toughness){ comparisonCriterion = toughness; diff --git a/projects/mtg/src/WEvent.cpp b/projects/mtg/src/WEvent.cpp index 5db94b92c..ef11f62eb 100644 --- a/projects/mtg/src/WEvent.cpp +++ b/projects/mtg/src/WEvent.cpp @@ -18,6 +18,22 @@ WEventPhaseChange::WEventPhaseChange(Phase * from, Phase * to) : WEvent(CHANGE_P WEventCardTap::WEventCardTap(MTGCardInstance * card, bool before, bool after) : WEventCardUpdate(card), before(before), after(after){} +WEventCardTappedForMana::WEventCardTappedForMana(MTGCardInstance * card, bool before, bool after) : WEventCardUpdate(card), before(before), after(after){} + +WEventCardAttacked::WEventCardAttacked(MTGCardInstance * card) : WEventCardUpdate(card){} + +WEventCardAttackedNotBlocked::WEventCardAttackedNotBlocked(MTGCardInstance * card) : WEventCardUpdate(card){} + +WEventCardAttackedBlocked::WEventCardAttackedBlocked(MTGCardInstance * card) : WEventCardUpdate(card){} + +WEventCardBlocked::WEventCardBlocked(MTGCardInstance * card) : WEventCardUpdate(card){} + +WEventcardDraw::WEventcardDraw(Player * player,int nb_cards) : player(player), nb_cards(nb_cards){} + +WEventCardSacrifice::WEventCardSacrifice(MTGCardInstance * card) : WEventCardUpdate(card){} + +WEventCardDiscard::WEventCardDiscard(MTGCardInstance * card) : WEventCardUpdate(card){} + WEventCardChangeType::WEventCardChangeType(MTGCardInstance * card, int type, bool before, bool after) : WEventCardUpdate(card), type(type), before(before), after(after){} WEventCreatureAttacker::WEventCreatureAttacker(MTGCardInstance * card, Targetable * before, Targetable * after) : WEventCardUpdate(card), before(before), after(after){} @@ -51,11 +67,61 @@ Targetable * WEventZoneChange::getTarget(int target) { return NULL; } +Targetable * WEventCardAttacked::getTarget(int target) { + if (target) return card; + return NULL; +} + +Targetable * WEventCardSacrifice::getTarget(int target) { + if (target) return card; + return NULL; +} + +Targetable * WEventCardDiscard::getTarget(int target) { + if (target) return card; + return NULL; +} + +Targetable * WEventCardAttackedNotBlocked::getTarget(int target) { + if (target) return card; + return NULL; +} + +Targetable * WEventCardAttackedBlocked::getTarget(int target) { + switch (target) { + case TARGET_TO : + return card; + case TARGET_FROM : + return card->getNextOpponent(); + } + return NULL; +} + +Targetable * WEventCardBlocked::getTarget(int target) { + switch (target) { + case TARGET_TO : + return card; + case TARGET_FROM : + return card->getNextOpponent(); + } + return NULL; +} + Targetable * WEventCardTap::getTarget(int target){ if (target) return card; return NULL; } +Targetable * WEventCardTappedForMana::getTarget(int target){ + if (target) return card; + return NULL; +} + +Targetable * WEventcardDraw::getTarget(Player * player){ + if (player) return player; + return NULL; +} + std::ostream& WEvent::toString(std::ostream& out) const { return out << "EVENT";