From 0210ecf28c1ea2df14316b876a691d766f0526d2 Mon Sep 17 00:00:00 2001 From: "omegablast2002@yahoo.com" Date: Tue, 13 Mar 2012 17:54:24 +0000 Subject: [PATCH] ok this is the last commit but i had massive issues after chopping it all up, hopefully it doesn't hilight everything that was already commited...if it does i apologize my own patch started giving me conflicts.... im like 7 tries into commiting this part.... --- projects/mtg/include/AllAbilities.h | 141 ++++++- projects/mtg/include/MTGDefinitions.h | 6 +- projects/mtg/include/MTGRules.h | 36 ++ projects/mtg/src/AllAbilities.cpp | 514 +++++++++++++++++++++++++- projects/mtg/src/CardPrimitive.cpp | 2 - projects/mtg/src/DeckStats.cpp | 2 +- projects/mtg/src/GuiCombat.cpp | 27 +- projects/mtg/src/GuiPlay.cpp | 4 +- projects/mtg/src/MTGAbility.cpp | 461 ++++++++++++++++++----- projects/mtg/src/MTGCardInstance.cpp | 1 - projects/mtg/src/MTGDefinitions.cpp | 6 +- projects/mtg/src/MTGRules.cpp | 160 +++++++- projects/mtg/src/ManaCost.cpp | 11 +- projects/mtg/src/Rules.cpp | 1 + projects/mtg/src/TargetChooser.cpp | 23 ++ 15 files changed, 1269 insertions(+), 126 deletions(-) diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index 0aa636af2..565bda926 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -1004,9 +1004,10 @@ class IfThenAbility: public InstantAbility { public: MTGAbility * delayedAbility; + MTGAbility * delayedElseAbility; int type; string Cond; - IfThenAbility(GameObserver* observer, int _id,MTGAbility * delayedAbility = NULL, MTGCardInstance * _source=NULL, Targetable * target = NULL, int type = 1,string Cond = ""); + IfThenAbility(GameObserver* observer, int _id,MTGAbility * delayedAbility = NULL,MTGAbility * delayedElseAbility = NULL, MTGCardInstance * _source=NULL, Targetable * target = NULL, int type = 1,string Cond = ""); int resolve(); const char * getMenuText(); IfThenAbility * clone() const; @@ -1110,6 +1111,16 @@ public: }; +//place a card on the bottom of owners library +class AALibraryBottom: public ActivatedAbility +{ +public: + AALibraryBottom(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target = NULL, ManaCost * _cost = NULL); + int resolve(); + const char * getMenuText(); + AALibraryBottom * clone() const; +}; + //Copier. ActivatedAbility class AACopier: public ActivatedAbility { @@ -2659,6 +2670,11 @@ public: if(sabilities.find("battleready") != string::npos) battleReady = true; + if(sabilities.find("chosencolor") != string::npos) + { + colors.push_back(source->chooseacolor); + } + for (int j = 0; j < Constants::NB_Colors; j++) { size_t found = sabilities.find(Constants::MTGColorStrings[j]); @@ -2674,12 +2690,21 @@ public: size_t found = s.find(" "); if (found != string::npos) { - int id = MTGAllCards::findType(s.substr(0, found)); + string toCheck = s.substr(0, found); + if(toCheck.find("chosentype") != string::npos || toCheck.find("Chosentype") != string::npos) + { + toCheck = source->chooseasubtype; + } + int id = MTGAllCards::findType(toCheck); types.push_back(id); s = s.substr(found + 1); } else { + if(s.find("chosentype") != string::npos || s.find("Chosentype") != string::npos) + { + s = source->chooseasubtype; + } int id = MTGAllCards::findType(s); types.push_back(id); s = ""; @@ -3406,6 +3431,17 @@ public: } }; +class AAExchangeLife: public ActivatedAbilityTP +{ +public: + AAExchangeLife(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, ManaCost * _cost = NULL, + int who = TargetChooser::UNSET); + int resolve(); + const char * getMenuText(); + AAExchangeLife * clone() const; + +}; + // 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 @@ -4951,9 +4987,6 @@ public: return NEW APhaseAlter(*this); } }; - -//--------------Addon Abra------------------ - //Basilik --> needs to be made more generic to avoid duplicate (also something like if opponent=type then ...) class ABasilik: public MTGAbility { @@ -5015,6 +5048,19 @@ public: AADepleter * clone() const; }; +//Generic skip turn/extra turn +class AAModTurn: public ActivatedAbilityTP +{ +public: + string nbTurnStr; + + AAModTurn(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbTurnStr, ManaCost * _cost = NULL, + int who = TargetChooser::UNSET); + int resolve(); + const char * getMenuText(); + AAModTurn * clone() const; +}; + //Shuffle class AAShuffle: public ActivatedAbilityTP { @@ -5283,6 +5329,15 @@ public: int resolve() { MTGCardInstance * _theftTarget = (MTGCardInstance*)target; + bool recast = false; + if(!_theftTarget->isInPlay(game)) + { + recast = true; + } + while(_theftTarget->next) + { + _theftTarget= _theftTarget->next; + } if(_theftTarget) { TrueController = _theftTarget->controller(); @@ -5291,7 +5346,13 @@ public: target = copy; source->target = copy; copy->summoningSickness = 0; + if(recast) + { + Spell * spell = NEW Spell(game, copy); + spell->resolve(); + SAFE_DELETE(spell); } + } return 1; } @@ -5318,6 +5379,76 @@ public: } }; +//---------------------------------------------- +class AASetColorChosen: public InstantAbility +{ +public: + int color; + string abilityToAlter; + MTGAbility * abilityAltered; + AASetColorChosen(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, int _color = 0 ,string toAdd = ""); + int resolve(); + const char* getMenuText(); + AASetColorChosen * clone() const; + ~AASetColorChosen(); +}; +class AASetTypeChosen: public InstantAbility +{ +public: + int type; + string menutext; + string abilityToAlter; + MTGAbility * abilityAltered; + AASetTypeChosen(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, int _type = 0,string menu = "error" ,string toAdd = ""); + int resolve(); + const char* getMenuText(); + AASetTypeChosen * clone() const; + ~AASetTypeChosen(); +}; +class GenericChooseTypeColor: public ActivatedAbility +{ +public: + string baseAbility; + bool chooseColor; + AASetColorChosen * setColor; + AASetTypeChosen * setType; + bool ANonWall; + GenericChooseTypeColor(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target, string toAdd = "",bool chooseColor = false,bool nonwall = false, ManaCost * cost = NULL); + int resolve(); + const char* getMenuText(); + GenericChooseTypeColor * clone() const; + ~GenericChooseTypeColor(); + +}; +//------------------------------------------------ +//flip a coin and call it, with win or lose abilities +class AASetCoin: public InstantAbility +{ +public: + int side; + string abilityToAlter; + string abilityWin; + string abilityLose; + MTGAbility * abilityAltered; + AASetCoin(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target, int side = -1,string toAdd = ""); + int resolve(); + const char* getMenuText(); + AASetCoin * clone() const; + ~AASetCoin(); +}; +class GenericFlipACoin: public ActivatedAbility +{ +public: + string baseAbility; + bool chooseColor; + AASetCoin * setCoin; + GenericFlipACoin(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target, string toAdd = "", ManaCost * cost = NULL); + int resolve(); + const char* getMenuText(); + GenericFlipACoin * clone() const; + ~GenericFlipACoin(); + +}; // utility functions void PopulateColorIndexVector(list& colors, const string& colorsString, char delimiter = ','); diff --git a/projects/mtg/include/MTGDefinitions.h b/projects/mtg/include/MTGDefinitions.h index 01536f659..c206726c2 100644 --- a/projects/mtg/include/MTGDefinitions.h +++ b/projects/mtg/include/MTGDefinitions.h @@ -212,8 +212,12 @@ class Constants HYDRA = 92, UNDYING = 93, POISONSHROUD = 94, + NOACTIVATED = 95, + NOACTIVATEDTAP = 96, + NOMANA = 97, + ONLYMANA = 98, - NB_BASIC_ABILITIES = 95, + NB_BASIC_ABILITIES = 99, RARITY_S = 'S', //Special Rarity diff --git a/projects/mtg/include/MTGRules.h b/projects/mtg/include/MTGRules.h index cf8457a43..21d8bae04 100644 --- a/projects/mtg/include/MTGRules.h +++ b/projects/mtg/include/MTGRules.h @@ -203,6 +203,33 @@ public: virtual MTGAttackRule * clone() const; }; + +class MTGPlaneswalkerAttackRule: public PermanentAbility, public Limitor +{ +public: + + virtual bool select(Target*); + virtual bool greyout(Target*); + int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); + int reactToClick(MTGCardInstance * card); + MTGPlaneswalkerAttackRule(GameObserver* observer, int _id); + const char * getMenuText() + { + return "Attack Planeswalker"; + } + virtual MTGPlaneswalkerAttackRule * clone() const; +}; +class AAPlaneswalkerAttacked: public InstantAbility +{ +public: + string menuText; + MTGCardInstance* attacker; + AAPlaneswalkerAttacked(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * target); + int resolve(); + const char* getMenuText(); + AAPlaneswalkerAttacked * clone() const; + ~AAPlaneswalkerAttacked(); +}; /* handles combat trigger send recieve events*/ class MTGCombatTriggersRule: public PermanentAbility { @@ -292,7 +319,16 @@ public: virtual ostream& toString(ostream& out) const; virtual MTGPlaneWalkerRule * clone() const; }; +/* LifeLink */ +class MTGPlaneswalkerDamage: public PermanentAbility +{ +public: + MTGPlaneswalkerDamage(GameObserver* observer, int _id); + int receiveEvent(WEvent * event); + + virtual MTGPlaneswalkerDamage * clone() const; +}; class MTGMomirRule: public PermanentAbility { private: diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index f89932b26..7464fb292 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -183,6 +183,7 @@ AADamager::AADamager(GameObserver* observer, int _id, MTGCardInstance * _source, ActivatedAbilityTP(observer, _id, _source, _target, _cost, who), d(d) { aType = MTGAbility::DAMAGER; + redirected = false; } int AADamager::resolve() @@ -191,6 +192,33 @@ AADamager::AADamager(GameObserver* observer, int _id, MTGCardInstance * _source, if (_target) { WParsedInt damage(d, NULL, (MTGCardInstance *)source); + if(_target == game->opponent() && game->opponent()->inPlay()->hasType("planeswalker") && !redirected) + { + vectorselection; + MTGCardInstance * check = NULL; + this->redirected = true; + MTGAbility * setPlayer = this->clone(); + selection.push_back(setPlayer); + int checkWalkers = ((Player*)_target)->game->battlefield->cards.size(); + for(int i = 0; i < checkWalkers;++i) + { + check = ((Player*)_target)->game->battlefield->cards[i]; + if(check->hasType(Subtypes::TYPE_PLANESWALKER)) + { + MTGAbility * setWalker = this->clone(); + setWalker->oneShot = true; + setWalker->target = check; + selection.push_back(setWalker); + } + } + if(selection.size()) + { + MTGAbility * a1 = NEW MenuAbility(game, this->GetId(), source, source,true,selection); + game->mLayers->actionLayer()->currentActionCard = source; + a1->resolve(); + } + return 1; + } game->mLayers->stackLayer()->addDamage(source, _target, damage.getValue()); game->mLayers->stackLayer()->resolve(); return 1; @@ -206,6 +234,11 @@ AADamager::AADamager(GameObserver* observer, int _id, MTGCardInstance * _source, const char * AADamager::getMenuText() { + MTGCardInstance * _target = dynamic_cast(target); + if(_target && _target->hasType(Subtypes::TYPE_PLANESWALKER)) + return _target->name.c_str(); + if(redirected) + return "Damage Player"; return "Damage"; } @@ -248,6 +281,83 @@ AADepleter * AADepleter::clone() const return NEW AADepleter(*this); } +//take extra turns or skip turns, values in the negitive will make you skip. +AAModTurn::AAModTurn(GameObserver* observer, int _id, MTGCardInstance * card, Targetable * _target,string nbTurnStr, ManaCost * _cost, int who) : + ActivatedAbilityTP(observer, _id, card, _target, _cost, who),nbTurnStr(nbTurnStr) +{ + +} + int AAModTurn::resolve() + { + Player * player = getPlayerFromTarget(getTarget()); + if (player) + { + WParsedInt numTurns(nbTurnStr, NULL, source); + if(numTurns.getValue() > 0) + { + player->extraTurn += numTurns.getValue(); + } + else + { + player->skippingTurn += abs(numTurns.getValue()); + + } + } + return 1; + } + + const char * AAModTurn::getMenuText() + { + WParsedInt numTurns(nbTurnStr, NULL, source); + if(numTurns.getValue() > 0) + return "Take Extra Turn(s)"; + else + return "Skip A Turn(s)"; + } + +AAModTurn * AAModTurn::clone() const +{ + return NEW AAModTurn(*this); +} + +//move target to bottom of owners library +AALibraryBottom::AALibraryBottom(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) : +ActivatedAbility(observer, _id, _source, _cost, 0) +{ + target = _target; +} + +int AALibraryBottom::resolve() +{ + MTGCardInstance * _target = (MTGCardInstance *) target; + if (_target = _target->owner->game->putInLibrary(_target)) + { + MTGLibrary * library = _target->owner->game->library; + vectoroldOrder = library->cards; + vectornewOrder; + newOrder.push_back(_target); + for(unsigned int k = 0;k < oldOrder.size();++k) + { + MTGCardInstance * rearranged = oldOrder[k]; + if(rearranged != _target) + newOrder.push_back(rearranged); + } + library->cards = newOrder; + return 1; + } + return 0; +} + +const char * AALibraryBottom::getMenuText() +{ + return "Bottom Of Library"; +} + +AALibraryBottom * AALibraryBottom::clone() const +{ + return NEW AALibraryBottom(*this); +} + //AACopier AACopier::AACopier(GameObserver* observer, int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost) : ActivatedAbility(observer, _id, _source, _cost, 0) @@ -670,8 +780,318 @@ AAProliferate * AAProliferate::clone() const AAProliferate::~AAProliferate() { } +// +//choosing a type or color +GenericChooseTypeColor::GenericChooseTypeColor(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target,string _toAdd,bool chooseColor,bool nonwall, ManaCost * cost) : +ActivatedAbility(observer, id, source, cost, 0), baseAbility(_toAdd),chooseColor(chooseColor),ANonWall(nonwall) +{ + this->GetId(); + setColor = NULL; +} +int GenericChooseTypeColor::resolve() +{ + if (!target) + return 0; + vectorselection; + if(chooseColor) + { + for (int i = Constants::MTG_COLOR_GREEN; i <= Constants::MTG_COLOR_WHITE; ++i) + { + setColor = NEW AASetColorChosen(game, game->mLayers->actionLayer()->getMaxId(), source,(MTGCardInstance*)target, i, baseAbility); + MTGAbility * set = setColor->clone(); + set->oneShot = true; + selection.push_back(set); + SAFE_DELETE(setColor); + } + } + else + { + vector values = MTGAllCards::getCreatureValuesById(); + for (size_t i = 0; i < values.size(); ++i) + { + string menu = values[i]; + if(!ANonWall || (menu != "wall" && menu != "Wall")) + { + setType = NEW AASetTypeChosen(game, game->mLayers->actionLayer()->getMaxId(), source,(MTGCardInstance*)target, i,menu,baseAbility); + MTGAbility * set = setType->clone(); + set->oneShot = true; + selection.push_back(set); + SAFE_DELETE(setType); + } + } + } + + if(selection.size()) + { + MTGAbility * a1 = NEW MenuAbility(game, this->GetId(), target, source,true,selection); + game->mLayers->actionLayer()->currentActionCard = (MTGCardInstance *)target; + a1->resolve(); + } + return 1; + +} + +const char* GenericChooseTypeColor::getMenuText() +{ + if(chooseColor) + return "Choose a color"; + else + return "Choose a type"; +} + +GenericChooseTypeColor * GenericChooseTypeColor::clone() const +{ + GenericChooseTypeColor * a = NEW GenericChooseTypeColor(*this); + return a; +} + +GenericChooseTypeColor::~GenericChooseTypeColor() +{ +} + +//set color choosen + AASetColorChosen::AASetColorChosen(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target,int _color , string toAlter): + InstantAbility(observer, id, source),color(_color), abilityToAlter(toAlter) +{ + this->target = _target; + abilityAltered = NULL; +} +int AASetColorChosen::resolve() +{ + MTGCardInstance * _target = (MTGCardInstance *)target; + _target->chooseacolor = color; + + if(abilityToAlter.size()) + { + AbilityFactory af(game); + abilityAltered = af.parseMagicLine(abilityToAlter, 0, NULL, _target); + if(!abilityAltered) + return 0; + abilityAltered->canBeInterrupted = false; + if(abilityAltered->oneShot) + { + abilityAltered->resolve(); + SAFE_DELETE(abilityAltered); + } + else + { + abilityAltered->target = _target; + MayAbility * dontAdd = dynamic_cast(abilityAltered); + if (!dontAdd) + { + _target->cardsAbilities.push_back(abilityAltered); + for(unsigned int j = 0;j < _target->cardsAbilities.size();++j) + { + if(_target->cardsAbilities[j] == this) + _target->cardsAbilities.erase(_target->cardsAbilities.begin() + j); + } + } + abilityAltered->addToGame(); + } + _target->skipDamageTestOnce = true;//some cards rely on this ability updating before damage test are run. otherwise they die before toughnes bonus applies. + } + return 1; +} + +const char* AASetColorChosen::getMenuText() +{ + return Constants::MTGColorStrings[color]; +} + +AASetColorChosen * AASetColorChosen::clone() const +{ + return NEW AASetColorChosen(*this); +} + +AASetColorChosen::~AASetColorChosen() +{ +} + +//set type choosen + AASetTypeChosen::AASetTypeChosen(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target,int _type ,string _menu,string toAlter): + InstantAbility(observer, id, source),type(_type), abilityToAlter(toAlter), menutext(_menu) +{ + this->target = _target; + abilityAltered = NULL; +} +int AASetTypeChosen::resolve() +{ + MTGCardInstance * _target = (MTGCardInstance *)target; + string typeChoosen = menutext; + _target->chooseasubtype = typeChoosen; + + if(abilityToAlter.size()) + { + AbilityFactory af(game); + abilityAltered = af.parseMagicLine(abilityToAlter, 0, NULL, _target); + if(abilityAltered->oneShot) + { + abilityAltered->resolve(); + SAFE_DELETE(abilityAltered); + } + else + { + abilityAltered->target = _target; + MayAbility * dontAdd = dynamic_cast(abilityAltered); + if (!dontAdd) + { + _target->cardsAbilities.push_back(abilityAltered); + for(unsigned int j = 0;j < _target->cardsAbilities.size();++j) + { + if(_target->cardsAbilities[j] == this) + _target->cardsAbilities.erase(_target->cardsAbilities.begin() + j); + } + } + + abilityAltered->addToGame(); + } + _target->skipDamageTestOnce = true;//some cards rely on this ability updating before damage test are run. otherwise they die before toughnes bonus applies. + } + return 1; +} + +const char* AASetTypeChosen::getMenuText() +{ + return menutext.c_str(); +} + +AASetTypeChosen * AASetTypeChosen::clone() const +{ + return NEW AASetTypeChosen(*this); +} + +AASetTypeChosen::~AASetTypeChosen() +{ +} // +//choosing a type or color +GenericFlipACoin::GenericFlipACoin(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target,string _toAdd, ManaCost * cost) : +ActivatedAbility(observer, id, source, cost, 0), baseAbility(_toAdd),chooseColor(chooseColor) +{ + this->GetId(); + setCoin = NULL; +} + +int GenericFlipACoin::resolve() +{ + if (!target) + return 0; + vectorselection; + for (int i = 0; i <2; ++i) + { + setCoin = NEW AASetCoin(game, game->mLayers->actionLayer()->getMaxId(), source,(MTGCardInstance*)target, i, baseAbility); + MTGAbility * set = setCoin->clone(); + set->oneShot = true; + selection.push_back(set); + SAFE_DELETE(setCoin); + } + + if(selection.size()) + { + MTGAbility * a1 = NEW MenuAbility(game, this->GetId(), target, source,false,selection); + game->mLayers->actionLayer()->currentActionCard = (MTGCardInstance *)target; + a1->resolve(); + } + return 1; + +} + +const char* GenericFlipACoin::getMenuText() +{ +return "Flip A Coin"; +} + +GenericFlipACoin * GenericFlipACoin::clone() const +{ + GenericFlipACoin * a = NEW GenericFlipACoin(*this); + return a; +} + +GenericFlipACoin::~GenericFlipACoin() +{ +} + +//set color choosen + AASetCoin::AASetCoin(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target,int _side , string toAlter): + InstantAbility(observer, id, source),side(_side), abilityToAlter(toAlter) +{ + this->target = _target; + abilityAltered = NULL; +} + +int AASetCoin::resolve() +{ + MTGCardInstance * _target = (MTGCardInstance *)target; + _target->coinSide = side; + + int flip = game->getRandomGenerator()->random() % 2; + vectorWin = parseBetween(abilityToAlter,"winability "," winabilityend"); + if(Win.size()) + { + abilityWin = Win[1]; + } + vectorLose = parseBetween(abilityToAlter,"loseability "," loseabilityend"); + if(Lose.size()) + { + abilityLose = Lose[1]; + } + + if(abilityWin.size() && flip == side) + { + AbilityFactory af(game); + abilityAltered = af.parseMagicLine(abilityWin, 0, NULL, _target); + abilityAltered->canBeInterrupted = false; + if(abilityAltered->oneShot) + { + abilityAltered->resolve(); + SAFE_DELETE(abilityAltered); + } + else + { + abilityAltered->addToGame(); + } + MTGAbility * message = NEW MTGEventText(game,this->GetId(), source, "You Won The Flip"); + message->oneShot = true; + message->addToGame(); + } + else if(abilityLose.size() && flip != side) + { + AbilityFactory af(game); + abilityAltered = af.parseMagicLine(abilityLose, 0, NULL, _target); + abilityAltered->canBeInterrupted = false; + if(abilityAltered->oneShot) + { + abilityAltered->resolve(); + SAFE_DELETE(abilityAltered); + } + else + { + abilityAltered->addToGame(); + } + MTGAbility * message = NEW MTGEventText(game,this->GetId(), source, "You Lost The Flip"); + message->oneShot = true; + message->addToGame(); + } + _target->skipDamageTestOnce = true; + return 1; +} + +const char* AASetCoin::getMenuText() +{ + if(side == 1) + return "Tails"; + return "Heads"; +} + +AASetCoin * AASetCoin::clone() const +{ + return NEW AASetCoin(*this); +} + +AASetCoin::~AASetCoin() +{ +} //Reset Damage on creatures AAResetDamage::AAResetDamage(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target, ManaCost * cost): @@ -2548,8 +2968,8 @@ ANewAffinity * ANewAffinity::clone() const } //IfThenEffect -IfThenAbility::IfThenAbility(GameObserver* observer, int _id, MTGAbility * delayedAbility, MTGCardInstance * _source, Targetable * _target, int type,string Cond) : -InstantAbility(observer, _id, _source),delayedAbility(delayedAbility), type(type),Cond(Cond) +IfThenAbility::IfThenAbility(GameObserver* observer, int _id, MTGAbility * delayedAbility, MTGAbility * delayedElseAbility, MTGCardInstance * _source, Targetable * _target, int type,string Cond) : +InstantAbility(observer, _id, _source),delayedAbility(delayedAbility),delayedElseAbility(delayedElseAbility), type(type),Cond(Cond) { target = _target; } @@ -2575,15 +2995,22 @@ int IfThenAbility::resolve() } } + MTGAbility * a1 = NULL; if((checkCond && type == 1)||(!checkCond && type == 2)) { - MTGAbility * a1 = delayedAbility->clone(); - if (!a1) - return 0; - + a1 = delayedAbility->clone(); + } + else if(delayedElseAbility) + { + delayedElseAbility->clone(); + } + if (!a1) + return 0; + else + { if(a1->target && !dynamic_cast(a1->target)) a1->target = aTarget; - + if(a1->oneShot) { a1->resolve(); @@ -2820,6 +3247,9 @@ MenuAbility::~MenuAbility() { for(int i = 0;i < int(abilities.size());i++) { + AASetColorChosen * chooseA = dynamic_cast(abilities[i]); + if(chooseA && chooseA->abilityAltered) + SAFE_DELETE(chooseA->abilityAltered); SAFE_DELETE(abilities[i]); } } @@ -3128,6 +3558,10 @@ ATransformer::ATransformer(GameObserver* observer, int id, MTGCardInstance * sou PopulateAbilityIndexVector(abilities, sabilities); PopulateColorIndexVector(colors, sabilities); + if(sabilities.find("chosencolor") != string::npos) + { + colors.push_back(source->chooseacolor); + } //this subkeyword adds a color without removing the existing colors. addNewColors = (sabilities.find("newcolors") != string::npos); @@ -3148,6 +3582,10 @@ ATransformer::ATransformer(GameObserver* observer, int id, MTGCardInstance * sou } else { + if(stypes.find("chosentype") != string::npos) + { + stypes = source->chooseasubtype; + } PopulateSubtypesIndexVector(types, stypes); } @@ -3520,6 +3958,68 @@ ASwapPTUEOT::~ASwapPTUEOT() SAFE_DELETE(ability); } +//exhange life with targetchooser +AAExchangeLife::AAExchangeLife(GameObserver* observer, int _id, MTGCardInstance * _source, Targetable * _target, ManaCost * _cost, + int who) : +ActivatedAbilityTP(observer, _id, _source, _target, _cost, who) +{ +} + +int AAExchangeLife::resolve() +{ + Damageable * _target = (Damageable *) getTarget(); + if (_target) + { + Player *player = source->controller(); + int oldlife = player->getLife(); + int targetOldLife = _target->getLife(); + int modifier = oldlife > targetOldLife? oldlife - targetOldLife:targetOldLife - oldlife; + if (_target->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE) + { + int increaser = 0; + MTGCardInstance * card = ((MTGCardInstance*)_target); + int toughMod = 0; + targetOldLife <= card->origtoughness?toughMod = card->origtoughness - targetOldLife: toughMod = targetOldLife - card->origtoughness; + if(oldlife > targetOldLife) + { + increaser = oldlife - targetOldLife; + player->gainOrLoseLife(modifier * -1); + card->addToToughness(increaser+toughMod); + } + else + { + _target->life = oldlife; + card->toughness = oldlife; + player->gainOrLoseLife(modifier); + } + + return 1; + } + Player * opponent = (Player*)_target; + if(oldlife > targetOldLife) + { + player->gainOrLoseLife(modifier * -1); + opponent->gainOrLoseLife(modifier); + } + else + { + player->gainOrLoseLife(modifier); + opponent->gainOrLoseLife(modifier * -1); + } + return 1; + } + return 0; +} + +const char * AAExchangeLife::getMenuText() +{ + return "Exchange life"; +} + +AAExchangeLife * AAExchangeLife::clone() const +{ + return NEW AAExchangeLife(*this); +} //ALoseAbilities ALoseAbilities::ALoseAbilities(GameObserver* observer, int id, MTGCardInstance * source, MTGCardInstance * _target) : diff --git a/projects/mtg/src/CardPrimitive.cpp b/projects/mtg/src/CardPrimitive.cpp index cbfc8ddfe..acbf59fab 100644 --- a/projects/mtg/src/CardPrimitive.cpp +++ b/projects/mtg/src/CardPrimitive.cpp @@ -37,8 +37,6 @@ CardPrimitive::CardPrimitive() CardPrimitive::CardPrimitive(CardPrimitive * source) { - if(!source) - return; if(!source) return; basicAbilities = source->basicAbilities; diff --git a/projects/mtg/src/DeckStats.cpp b/projects/mtg/src/DeckStats.cpp index d13355049..e9dd47f91 100644 --- a/projects/mtg/src/DeckStats.cpp +++ b/projects/mtg/src/DeckStats.cpp @@ -5,7 +5,7 @@ #include "Player.h" #include "GameObserver.h" #include "MTGDeck.h" -#include "ManaCostHybrid.h" +#include "ManacostHybrid.h" DeckStats * DeckStats::mInstance = NULL; diff --git a/projects/mtg/src/GuiCombat.cpp b/projects/mtg/src/GuiCombat.cpp index aaae9f013..f664d40ae 100644 --- a/projects/mtg/src/GuiCombat.cpp +++ b/projects/mtg/src/GuiCombat.cpp @@ -455,18 +455,37 @@ void GuiCombat::Render() (*it)->Render(step); if (activeAtk) { + float setH = 0; + float setW = 0; signed damage = activeAtk->card->stepPower(step); for (vector::iterator q = activeAtk->blockers.begin(); q != activeAtk->blockers.end(); ++q) { (*q)->Render(step); damage -= (*q)->sumDamages(); + setH = (*q)->Height; + setW = (*q)->Width; } if (damage < 0) damage = 0; if (activeAtk->card->has(Constants::TRAMPLE)) { - observer->opponent()->getIcon()->SetHotSpot(18, 25); - enemy_avatar.Render(observer->opponent()->getIcon().get()); + if(activeAtk->card->isAttacking && activeAtk->card->isAttacking != observer->opponent()) + { + JQuadPtr enemy = WResourceManager::Instance()->RetrieveCard((MTGCardInstance*)activeAtk->card->isAttacking, CACHE_THUMB); + float oldH = enemy->mHeight; + float oldW = enemy->mWidth; + enemy->mHeight = setH; + enemy->mWidth = setW; + enemy->SetHotSpot(18, 25); + enemy_avatar.Render(enemy.get()); + enemy->mHeight = oldH; + enemy->mWidth = oldW; + } + else + { + observer->opponent()->getIcon()->SetHotSpot(18, 25); + enemy_avatar.Render(observer->opponent()->getIcon().get()); + } WFont * mFont = WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT); mFont->SetColor(ARGB(255, 255, 64, 0)); { @@ -474,6 +493,7 @@ void GuiCombat::Render() sprintf(buf, "%i", damage); mFont->DrawString(buf, enemy_avatar.actX - 25, enemy_avatar.actY - 40); } + } } if (ok_tex) @@ -506,7 +526,8 @@ int GuiCombat::resolve() // Returns the number of damage objects dealt this turn } if (dmg > 0 && ((!attacker->isBlocked()) || attacker->has(Constants::TRAMPLE))) - stack->Add(NEW Damage(observer, (*it)->card, observer->opponent(), dmg, DAMAGE_COMBAT)); + stack->Add(NEW Damage(observer, (*it)->card, (Damageable*)attacker->isAttacking?(Damageable*)attacker->isAttacking:observer->opponent(), dmg, DAMAGE_COMBAT)); + for (vector::iterator d = (*it)->damages.begin(); d != (*it)->damages.end(); ++d) stack->Add(NEW Damage(*d)); } diff --git a/projects/mtg/src/GuiPlay.cpp b/projects/mtg/src/GuiPlay.cpp index 208728cd4..28f0518a0 100644 --- a/projects/mtg/src/GuiPlay.cpp +++ b/projects/mtg/src/GuiPlay.cpp @@ -190,7 +190,7 @@ void GuiPlay::Replace() for (iterator it = cards.begin(); it != end_spells; ++it) if (!(*it)->card->target) { - if(!(*it)->card->hasSubtype(Subtypes::TYPE_AURA) && !(*it)->card->hasType(Subtypes::TYPE_PLANESWALKER)) + if((!(*it)->card->hasSubtype(Subtypes::TYPE_AURA)|| ((*it)->card->hasSubtype(Subtypes::TYPE_AURA) && (*it)->card->playerTarget)) && !(*it)->card->hasType(Subtypes::TYPE_PLANESWALKER)) { if (observer->players[0] == (*it)->card->controller()) ++selfSpellsN; @@ -226,7 +226,7 @@ void GuiPlay::Replace() for (iterator it = cards.begin(); it != end_spells; ++it) if (!(*it)->card->target) { - if(!(*it)->card->hasSubtype(Subtypes::TYPE_AURA) && !(*it)->card->hasType(Subtypes::TYPE_PLANESWALKER)) + if((!(*it)->card->hasSubtype(Subtypes::TYPE_AURA)|| ((*it)->card->hasSubtype(Subtypes::TYPE_AURA) && (*it)->card->playerTarget)) && !(*it)->card->hasType(Subtypes::TYPE_PLANESWALKER)) { if (observer->players[0] == (*it)->card->controller()) selfSpells.Enstack(*it); diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index b4244cdb8..5c01cbd36 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -423,58 +423,59 @@ Counter * AbilityFactory::parseCounter(string s, MTGCardInstance * target, Spell int nb = 1; int maxNb = 0; string name = ""; - size_t start = 0; - size_t end = s.length(); - size_t separator = s.find(",", start); - if (separator == string::npos) - separator = s.find(".", start); - if (separator != string::npos) - { - size_t separator2 = s.find(",", separator + 1); - if (separator2 == string::npos) - separator2 = s.find(".", separator + 1); - size_t separator3 = string::npos; - if (separator2 != string::npos) - { - name = s.substr(separator2 + 1, end - separator2 - 1); - separator3 = s.find(",", separator2 + 1); - if (separator3 != string::npos) - { - name = s.substr(separator2 + 1,separator3 - separator2 - 1); - } - } - string nbstr = s.substr(separator + 1, separator2 - separator - 1); - WParsedInt * wpi; - if (target) - { - wpi = NEW WParsedInt(nbstr, spell, target); - } - else - { - wpi = NEW WParsedInt(atoi(nbstr.c_str())); - } - nb = wpi->getValue(); - delete (wpi); - string maxNbstr; - if (separator3 != string::npos) - { - maxNbstr = s.substr(separator3 + 1, end - separator3 - 1); - WParsedInt * wpinb; - if (target) - { - wpinb = NEW WParsedInt(maxNbstr, spell, target); - } - else - { - wpinb = NEW WParsedInt(atoi(maxNbstr.c_str())); - } - maxNb = wpinb->getValue(); - delete(wpinb); - } - end = separator; - } + string nbstr = "1"; + string maxNbstr = "0"; + string spt = ""; + + vectorsplitCounter = split(s,','); + vectorsplitCounterCheck = split(s,'.'); + if(splitCounter.size() < splitCounterCheck.size()) + { + splitCounter = splitCounterCheck;//use the one with the most results. + } + if(!splitCounter.size()) + { + return NULL; + } + if(splitCounter.size() > 0)//counter(1/1) + { + spt = splitCounter[0]; + } + if(splitCounter.size() > 1)//counter(1/1,1) + { + nbstr = splitCounter[1]; + } + if(splitCounter.size() > 2)//counter(0/0,1,charge) + { + name = splitCounter[2]; + } + if(splitCounter.size() > 3)//counter(0/0,1,charge,2) + { + maxNbstr = splitCounter[3]; + } + WParsedInt * wpi; + if (target) + { + wpi = NEW WParsedInt(nbstr, spell, target); + } + else + { + wpi = NEW WParsedInt(atoi(nbstr.c_str())); + } + nb = wpi->getValue(); + delete (wpi); + WParsedInt * wpinb; + if (target) + { + wpinb = NEW WParsedInt(maxNbstr, spell, target); + } + else + { + wpinb = NEW WParsedInt(atoi(maxNbstr.c_str())); + } + maxNb = wpinb->getValue(); + delete(wpinb); - string spt = s.substr(start, end - start); int power, toughness; if (parsePowerToughness(spt, &power, &toughness)) { @@ -488,22 +489,19 @@ Counter * AbilityFactory::parseCounter(string s, MTGCardInstance * target, Spell int AbilityFactory::parsePowerToughness(string s, int *power, int *toughness) { - size_t found = s.find("/"); - if (found != string::npos) + vectorsplitPT = split(s,'/'); + vectorsplitPTCheck = split(s,'%'); + if(splitPT.size() < 2 && splitPT.size() < splitPTCheck.size()) { - size_t end = s.find(" ", found); - if (end == string::npos) - end = s.size(); - size_t start = s.find_last_of(" ", found); - if (start == string::npos) - start = -1; - - *power = atoi(s.substr(start + 1, s.size() - found).c_str()); - *toughness = atoi(s.substr(found + 1, end - found - 1).c_str()); - - return 1; + splitPT = splitPTCheck; } - return 0; + if(!splitPT.size()) + { + return 0; + } + *power = atoi(splitPT[0].c_str()); + *toughness = atoi(splitPT[1].c_str()); + return 1; } TargetChooser * AbilityFactory::parseSimpleTC(const std::string& s, const std::string& _starter, MTGCardInstance * card, bool forceNoTarget) @@ -551,6 +549,7 @@ TriggeredAbility * AbilityFactory::parseTrigger(string s, string magicText, int bool opponentPoisoned = (s.find("opponentpoisoned") != string::npos); bool lifelost = (s.find("foelost(") != string::npos); int lifeamount = lifelost ? atoi(s.substr(s.find("foelost(") + 8,')').c_str()) : 0; + bool neverRemove = (s.find("dontremove") != string::npos); //Card Changed Zone found = s.find("movedto("); @@ -602,7 +601,13 @@ TriggeredAbility * AbilityFactory::parseTrigger(string s, string magicText, int fromTc = tcf.createTargetChooser(starget, card); fromTc->targetter = NULL; //avoid protection from } - return NEW TrCardAddedToZone(observer, id, card, (TargetZoneChooser *) toTc, toTcCard, (TargetZoneChooser *) fromTc, fromTcCard,once,sourceUntapped,isSuspended); + TriggeredAbility * mover = NEW TrCardAddedToZone(observer, id, card, (TargetZoneChooser *) toTc, toTcCard, (TargetZoneChooser *) fromTc, fromTcCard,once,sourceUntapped,isSuspended); + if(neverRemove) + { + mover->forcedAlive = 1; + mover->forceDestroy = -1; + } + return mover; } //Card unTapped @@ -922,6 +927,19 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG observer->addObserver(NEW MTGPlaneWalkerRule(observer, -1)); return NULL; } + found = s.find("planeswalkerdamage"); + if(found != string::npos) + { + observer->addObserver(NEW MTGPlaneswalkerDamage(observer, -1)); + return NULL; + } + found = s.find("planeswalkerattack"); + if(found != string::npos) + { + observer->addObserver(NEW MTGPlaneswalkerAttackRule(observer, -1)); + return NULL; + } + //this handles the clean up of tokens !!MUST BE ADDED BEFORE PERSIST RULE!! found = s.find("tokencleanuprule"); if(found != string::npos) @@ -965,10 +983,21 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return NULL; } + + if(strncmp(s.c_str(), "chooseacolor ", strlen("chooseacolor ")) == 0 || strncmp(s.c_str(), "chooseatype ", strlen("chooseatype ")) == 0) + { + MTGAbility * choose = parseChooseActionAbility(s,card,spell,target,0,id); + choose = NEW GenericActivatedAbility(observer, "","",id, card,choose,NULL); + MayAbility * mainAbility = NEW MayAbility(observer, id, choose, card,true); + return mainAbility; + } + //need to remove the section inside the transforms ability from the string before parsing //TODO: store string values of "&&" so we can remove the classes added just to add support //the current parser finds other abilities inside what should be nested abilities, and converts them into //actual abilities, this is a limitation. + string unchangedS = ""; + unchangedS.append(s); found = s.find("transforms(("); if (found != string::npos && storedString.empty()) { @@ -1040,7 +1069,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG TargetChooser * tc = NULL; string sWithoutTc = s; - + string tcString = ""; //Target Abilities - We also handle the case "notatarget" here, for things such as copy effects bool isTarget = true; vector splitTarget = parseBetween(s, "notatarget(", ")"); @@ -1053,6 +1082,8 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG { TargetChooserFactory tcf(observer); tc = tcf.createTargetChooser(splitTarget[1], card); + tcString = splitTarget[1]; + if (!isTarget) tc->targetter = NULL; sWithoutTc = splitTarget[0]; @@ -1139,7 +1170,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG if (tc) { tc->belongsToAbility = sWithoutTc; - return NEW GenericTargetAbility(observer, newName,castRestriction,id, card, tc, a, cost, limit,sideEffect,usesBeforeSideEffect, restrictions, dest); + return NEW GenericTargetAbility(observer, newName,castRestriction,id, card, tc, a, cost, limit,sideEffect,usesBeforeSideEffect, restrictions, dest,tcString); } return NEW GenericActivatedAbility(observer, newName,castRestriction,id, card, a, cost, limit,sideEffect,usesBeforeSideEffect,restrictions, dest); } @@ -1171,10 +1202,20 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG if (sWithoutTc.find(ifKeywords[i]) == 0) { string cond = sWithoutTc.substr(ifKeywords[i].length(),ifKeywords[i].length() + sWithoutTc.find(" then ")-6); - string s1 = s.substr(s.find(" then ")+6); + size_t foundElse = s.find(" else "); + MTGAbility * a2 = NULL; + if(foundElse != string::npos) + { + string s2 = s.substr(foundElse+6); + if(s2.size()) + s.erase(s.find(" else ")+1); + a2 = parseMagicLine(s2, id, spell, card); + } + string s1 = s.substr(foundElse+6); MTGAbility * a1 = parseMagicLine(s1, id, spell, card); + if(!a1) return NULL; - MTGAbility * a = NEW IfThenAbility(observer, id, a1, card,(Targetable*)target,checkIf[i],cond); + MTGAbility * a = NEW IfThenAbility(observer, id, a1,a2, card,(Targetable*)target,checkIf[i],cond); a->canBeInterrupted = false; a->oneShot = true; if(tc) @@ -1182,7 +1223,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return a; } } - + //When...comes into play, you may... //When...comes into play, choose one... const string mayKeywords[] = {"may ", "choice "}; @@ -1218,7 +1259,6 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return mainAbility; } } - // Generic "Until end of turn" effect if (s.find("ueot ") == 0) { @@ -1230,6 +1270,31 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return NEW GenericInstantAbility(observer, 1, card, (Damageable *) target, a1); } + // neverending effect + if (s.find("emblem ") == 0) + { + string s1 = s.substr(7); + MTGAbility * a1 = parseMagicLine(s1, id, spell, card); + if (!a1) + return NULL; + + return NEW GenericAbilityMod(observer, 1, card,spell?spell->getNextDamageableTarget():(Damageable *) target, a1);; + } + + //choose a color + vector splitChooseAColor = parseBetween(s, "activatechooseacolor ", " activatechooseend"); + if (splitChooseAColor.size()) + { + return parseChooseActionAbility(unchangedS,card,spell,target,restrictions,id); + } + + //choose a type + vector splitChooseAType = parseBetween(s, "activatechooseatype ", " activatechooseend"); + if (splitChooseAType.size()) + { + return parseChooseActionAbility(unchangedS,card,spell,target,restrictions,id); + } + //Upkeep Cost found = s.find("upcostmulti"); if (found != string::npos) @@ -1249,7 +1314,10 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG found = s.find("forcedalive"); if (found != string::npos) forcedalive = 1; - + bool neverRemove = false; + found = s.find("dontremove"); + if (found != string::npos) + neverRemove = true; //rather dirty way to stop thises and lords from conflicting with each other. size_t lord = string::npos; for (size_t j = 0; j < kLordKeywordsCount; ++j) @@ -1343,6 +1411,14 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG { result->oneShot = oneShot; a->forcedAlive = forcedalive; + if(neverRemove) + { + result->forceDestroy = -1; + result->forcedAlive = 1; + a->forceDestroy = -1; + a->forcedAlive = 1; + } + } return result; } @@ -1495,6 +1571,14 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG { result->oneShot = oneShot; a->forcedAlive = forcedalive; + if(neverRemove) + { + result->oneShot = false; + result->forceDestroy = -1; + result->forcedAlive = 1; + a->forceDestroy = -1; + a->forcedAlive = 1; + } } return result; } @@ -1585,6 +1669,18 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG { return parsePhaseActionAbility(s,card,spell,target,restrictions,id); } + + //flip a coin + vector splitFlipCoin = parseBetween(s, "flipacoin ", " flipend"); + if (splitFlipCoin.size()) + { + string a1 = splitFlipCoin[1]; + MTGAbility * a = NEW GenericFlipACoin(observer, id, card, target,a1); + a->oneShot = 1; + a->canBeInterrupted = false; + return a; + } + //Upkeep Cost found = s.find("upcost"); if (found != string::npos) @@ -1769,6 +1865,15 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return a; } + //put a card on bottom of library + found = s.find("bottomoflibrary"); + if (found != string::npos) + { + MTGAbility * a = NEW AALibraryBottom(observer, id, card, target); + a->oneShot = 1; + return a; + } + //Copy a target found = s.find("copy"); if (found != string::npos) @@ -1813,6 +1918,14 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG { MTGAbility *a = NEW AABuryCard(observer, id, card, target); a->oneShot = 1; + if(s.find("and(") != string::npos) + { + vector splitAnd = parseBetween(s, "and((", " ))",false); + if(splitAnd.size()) + { + ((AABuryCard*)a)->andAbility = parseMagicLine(splitAnd[1], id, spell, card); + } + } return a; } @@ -1820,6 +1933,14 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG { MTGAbility * a = NEW AADestroyCard(observer, id, card, target); a->oneShot = 1; + if(s.find("and(") != string::npos) + { + vector splitAnd = parseBetween(s, "and((", " ))",false); + if(splitAnd.size()) + { + ((AADestroyCard*)a)->andAbility = parseMagicLine(splitAnd[1], id, spell, card); + } + } return a; } @@ -1827,6 +1948,14 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG { MTGAbility *a = NEW AASacrificeCard(observer, id, card, target); a->oneShot = 1; + if(s.find("and(") != string::npos) + { + vector splitAnd = parseBetween(s, "and((", " ))",false); + if(splitAnd.size()) + { + ((AASacrificeCard*)a)->andAbility = parseMagicLine(splitAnd[1], id, spell, card); + } + } return a; } @@ -1834,6 +1963,14 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG { MTGAbility *a = NEW AADiscardCard(observer, id, card, target); a->oneShot = 1; + if(s.find("and(") != string::npos) + { + vector splitAnd = parseBetween(s, "and((", " ))",false); + if(splitAnd.size()) + { + ((AADiscardCard*)a)->andAbility = parseMagicLine(splitAnd[1], id, spell, card); + } + } return a; } bool oneShot = false; @@ -1984,6 +2121,16 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return a; } + //modify turns + vector splitModTurn = parseBetween(s, "turns:", " ", false); + if (splitModTurn.size()) + { + Targetable * t = spell ? spell->getNextTarget() : NULL; + MTGAbility * a = NEW AAModTurn(observer, id, card, t , splitModTurn[1], NULL, who); + a->oneShot = 1; + return a; + } + //Shuffle found = s.find("shuffle"); if (found != string::npos) @@ -2094,6 +2241,24 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return NULL; return NEW ABushidoAbility(observer, id, card,splitBushido[1]); } + vector splitPhaseAlter = parseBetween(s, "phasealter(", ")"); + if (splitPhaseAlter.size()) + { + string power, toughness; + vectorsplitPhaseAlter2 = split(splitPhaseAlter[1],','); + if(splitPhaseAlter2.size() < 3) + return NULL; + string after = ""; + if(splitPhaseAlter2.size() > 3) + { + vector splitPhaseAlterAfter = parseBetween(splitPhaseAlter2[3], "after<", ">"); + if(splitPhaseAlterAfter.size()) + after = splitPhaseAlterAfter[1]; + } + MTGAbility * a1 = NEW APhaseAlter(observer, id, card, target,splitPhaseAlter2[0].find("add") != string::npos, splitPhaseAlter2[1],splitPhaseAlter2[2], s.find("nextphase") != string::npos,after); + a1->canBeInterrupted = false; + return NEW GenericAbilityMod(observer, 1, card,spell?spell->getNextDamageableTarget():(Damageable *) target, a1); + } //loseAbilities if (s.find("loseabilities") != string::npos) @@ -2184,17 +2349,21 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG string newPower = ""; string newToughness = ""; bool ptFound = false; - if(becomesParameters.size() >1) - { - vector pt = split(becomesParameters[1], '/'); - newPower = pt[0]; - newToughness = pt[1]; - ptFound = true; - } + if(becomesParameters.size() >1) + { + vector pt = split(becomesParameters[1], '/'); + if(pt.size() > 1) + { + newPower = pt[0]; + newToughness = pt[1]; + ptFound = true; + } + } string sabilities = ""; - if(becomesParameters.size() > 2) + unsigned int becomesSize = ptFound?2:1; + if(becomesParameters.size() > becomesSize) { - for(unsigned int i = 2;i < becomesParameters.size();i++) + for(unsigned int i = becomesSize;i < becomesParameters.size();i++) { sabilities.append(becomesParameters[i].c_str()); if(i+1 < becomesParameters.size()) @@ -2339,7 +2508,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG found = s.find("add"); if (found != string::npos) { - ManaCost * output = ManaCost::parseManaCost(s.substr(found)); + ManaCost * output = ManaCost::parseManaCost(s.substr(found),NULL,card); Targetable * t = spell ? spell->getNextTarget() : NULL; MTGAbility * a = NEW AManaProducer(observer, id, card, t, output, NULL, who); a->oneShot = 1; @@ -2476,7 +2645,16 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG a->oneShot = 1; return a; } - + //exchange life with target; if creature then toughness is life. + found = s.find("exchangelife"); + if (found != string::npos) + { + Targetable * t = spell ? spell->getNextTarget() : NULL; + MTGAbility * a = NEW AAExchangeLife(observer, id, card, t, NULL, who); + a->oneShot = 1; + return a; + } + //Regeneration found = s.find("regenerate"); if (found != string::npos) @@ -2645,6 +2823,50 @@ MTGAbility * AbilityFactory::parsePhaseActionAbility(string s,MTGCardInstance * return NEW APhaseActionGeneric(observer, id, card,_target, trim(splitActions[2]), restrictions, phase,sourceinPlay,next,myturn,opponentturn,once); } +MTGAbility * AbilityFactory::parseChooseActionAbility(string s,MTGCardInstance * card,Spell * spell,MTGCardInstance * target, int restrictions,int id) +{ + vector splitChooseAColor2 = parseBetween(s, "activatechooseacolor ", " activatechooseend"); + if (splitChooseAColor2.size()) + { + string a1 = splitChooseAColor2[1]; + MTGAbility * a = NEW GenericChooseTypeColor(observer, id, card, target,a1,true); + a->oneShot = 1; + a->canBeInterrupted = false; + return a; + } + //choose a type + vector splitChooseAType2 = parseBetween(s, "activatechooseatype ", " activatechooseend"); + if (splitChooseAType2.size()) + { + string a1 = splitChooseAType2[1]; + MTGAbility * a = NEW GenericChooseTypeColor(observer, id, card, target,a1,false,s.find("nonwall")!=string::npos); + a->oneShot = 1; + a->canBeInterrupted = false; + return a; + } + //choose a color + vector splitChooseAColor = parseBetween(s, "chooseacolor ", " chooseend"); + if (splitChooseAColor.size()) + { + string a1 = splitChooseAColor[1]; + MTGAbility * a = NEW GenericChooseTypeColor(observer, id, card, target,a1,true); + a->oneShot = 1; + a->canBeInterrupted = false; + return a; + } + //choose a type + vector splitChooseAType = parseBetween(s, "chooseatype ", " chooseend"); + if (splitChooseAType.size()) + { + string a1 = splitChooseAType[1]; + MTGAbility * a = NEW GenericChooseTypeColor(observer, id, card, target,a1,false,s.find("nonwall")!=string::npos); + a->oneShot = 1; + a->canBeInterrupted = false; + return a; + } + return NULL; +} + //Tells the AI if the ability should target itself or an ennemy int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, TargetChooser * tc,Targetable * target) { @@ -3024,11 +3246,14 @@ int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int else { a->addToGame(); + MayAbility * dontAdd = dynamic_cast(a); + if(!dontAdd) + { if (a->source) a->source->cardsAbilities.push_back(a); else if(spell && spell->source) spell->source->cardsAbilities.push_back(a); - + } //keep track of abilities being added to the game on each card it belongs to, this ignores p/t bonuses given //from other cards, or ability bonuses, making it generally easier to strip a card of it's abilities. } @@ -3051,6 +3276,7 @@ void AbilityFactory::addAbilities(int _id, Spell * spell) zones->putInZone(card, spell->from, card->owner->game->graveyard); return; //fizzle } + card->playerTarget = spell->getNextPlayerTarget(); } _id = magicText(_id, spell); @@ -3818,7 +4044,6 @@ int ActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana) { if(card->isPhased) return 0; - Player * player = game->currentlyActing(); int cPhase = game->getCurrentGamePhase(); switch (restrictions) @@ -3886,7 +4111,44 @@ int ActivatedAbility::isReactingToClick(MTGCardInstance * card, ManaCost * mana) ManaCost * cost = getCost(); if (!cost) return 1; + if(card->hasType(Subtypes::TYPE_PLANESWALKER)) + { + for(unsigned int k = 0;k < card->cardsAbilities.size();++k) + { + ActivatedAbility * check = dynamic_cast(card->cardsAbilities[k]); + if(check && check->counters) + return 0; + } + if (player != game->currentPlayer) + return 0; + if (cPhase != MTG_PHASE_FIRSTMAIN && cPhase != MTG_PHASE_SECONDMAIN) + return 0; + } + if (source->has(Constants::NOACTIVATED)) + return 0; + AbilityFactory af(game); + MTGAbility * fmp = NULL; + fmp = af.getCoreAbility(this); + AManaProducer * amp = dynamic_cast (this); + AManaProducer * femp = dynamic_cast (fmp); + if (source->has(Constants::NOMANA) && (amp||femp)) + return 0; + if(source->has(Constants::ONLYMANA) && !(amp||femp)) + return 0; cost->setExtraCostsAction(this, card); + if (source->has(Constants::NOACTIVATEDTAP) && cost->extraCosts) + { + for(unsigned int i = 0;i < cost->extraCosts->costs.size();++i) + { + ExtraCost * eCost = cost->getExtraCost(i); + if(dynamic_cast(eCost)) + { + return 0; + } + } + + } + if (!mana) mana = player->getManaPool(); if (!mana->canAfford(cost)) @@ -4126,9 +4388,9 @@ int TargetAbility::resolve() Targetable * t = tc->getNextTarget(); if (t && ability) { - source->X = 0; if (abilityCost) { + source->X = 0; ManaCost * diff = abilityCost->Diff(getCost()); source->X = diff->hasX(); delete (diff); @@ -4275,20 +4537,19 @@ void InstantAbility::Update(float dt) InstantAbility::InstantAbility(GameObserver* observer, int _id, MTGCardInstance * source, Targetable * _target) : MTGAbility(observer, _id, source, _target) -{ - init = 0; -} + { + init = 0; + } //Instant abilities last generally until the end of the turn -int InstantAbility::testDestroy() -{ - GamePhase newPhase = game->getCurrentGamePhase(); - if (newPhase != currentPhase && newPhase == MTG_PHASE_AFTER_EOT) - return 1; - currentPhase = newPhase; - return 0; - -} + int InstantAbility::testDestroy() + { + GamePhase newPhase = game->getCurrentGamePhase(); + if (newPhase != currentPhase && newPhase == MTG_PHASE_AFTER_EOT) + return 1; + currentPhase = newPhase; + return 0; + } ostream& InstantAbility::toString(ostream& out) const { @@ -4328,9 +4589,7 @@ void ListMaintainerAbility::updateTargets() cards.erase(card); removed(card); } - temp.clear(); - //add New valid ones for (int i = 0; i < 2; i++) { diff --git a/projects/mtg/src/MTGCardInstance.cpp b/projects/mtg/src/MTGCardInstance.cpp index 781dd681a..68a6285a9 100644 --- a/projects/mtg/src/MTGCardInstance.cpp +++ b/projects/mtg/src/MTGCardInstance.cpp @@ -168,7 +168,6 @@ void MTGCardInstance::initMTGCI() flanked = 0; target = NULL; playerTarget = NULL; - playerTarget = NULL; type_as_damageable = DAMAGEABLE_MTGCARDINSTANCE; banding = NULL; owner = NULL; diff --git a/projects/mtg/src/MTGDefinitions.cpp b/projects/mtg/src/MTGDefinitions.cpp index 167db21b3..575bdc42d 100644 --- a/projects/mtg/src/MTGDefinitions.cpp +++ b/projects/mtg/src/MTGDefinitions.cpp @@ -123,7 +123,11 @@ const char* Constants::MTGBasicAbilities[] = { "canattack", "hydra", "undying", - "poisonshroud" + "poisonshroud", + "noactivatedability", + "notapability", + "nomanaability", + "onlymanaability" }; map Constants::MTGBasicAbilitiesMap; diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index ace1a8c8a..c55f3a8b6 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -1190,7 +1190,7 @@ int MTGAttackRule::receiveEvent(WEvent *e) for (int i = 0; i < z->nb_cards; i++) { MTGCardInstance * card = z->cards[i]; - if (!card->isAttacker() && card->has(Constants::MUSTATTACK)) + if (!card->isAttacker() && !event->from->isExtra && card->has(Constants::MUSTATTACK))//cards are only required to attack in the real attack phase of a turn. reactToClick(card); if (!card->isAttacker() && card->has(Constants::TREASON) && p->isAI()) reactToClick(card); @@ -1232,6 +1232,121 @@ MTGAttackRule * MTGAttackRule::clone() const { return NEW MTGAttackRule(*this); } +//handling for planeswalker attacking choice +MTGPlaneswalkerAttackRule::MTGPlaneswalkerAttackRule(GameObserver* observer, int _id) : +PermanentAbility(observer, _id) +{ + aType = MTGAbility::MTG_ATTACK_RULE; +} + +int MTGPlaneswalkerAttackRule::isReactingToClick(MTGCardInstance * card, ManaCost * mana) +{ + if (currentPhase == MTG_PHASE_COMBATATTACKERS && card->controller() == game->currentPlayer && card->controller() == game->currentlyActing())//on my turn and when I am the acting player. + { + if(!card->controller()->opponent()->game->inPlay->hasType("planeswalker")) + return 0; + if(card->isPhased) + return 0; + if (card->isAttacker()) + return 1; + if (card->canAttack()) + return 1; + } + return 0; +} + +int MTGPlaneswalkerAttackRule::reactToClick(MTGCardInstance * card) +{ + if (!isReactingToClick(card)) + return 0; + //Graphically select the next card that can attack + if (!card->isAttacker()) + { + game->getCardSelector()->PushLimitor(); + game->getCardSelector()->Limit(this, CardView::playZone); + game->getCardSelector()->CheckUserInput(JGE_BTN_RIGHT); + game->getCardSelector()->Limit(NULL, CardView::playZone); + game->getCardSelector()->PopLimitor(); + } + + vectorselection; + MTGCardInstance * check = NULL; + int checkWalkers = card->controller()->opponent()->game->battlefield->cards.size(); + for(int i = 0; i < checkWalkers;++i) + { + check = card->controller()->opponent()->game->battlefield->cards[i]; + if(check->hasType(Subtypes::TYPE_PLANESWALKER)) + { + MTGAbility * setPw = NEW AAPlaneswalkerAttacked(game, game->mLayers->actionLayer()->getMaxId(), card,check); + MTGAbility * setWalker = setPw->clone(); + setWalker->oneShot = true; + selection.push_back(setWalker); + SAFE_DELETE(setPw); + } + } + + + if(selection.size()) + { + MTGAbility * a1 = NEW MenuAbility(game, this->GetId(), card, card,false,selection); + game->mLayers->actionLayer()->currentActionCard = card; + a1->resolve(); + } + + return 1; +} + +MTGPlaneswalkerAttackRule * MTGPlaneswalkerAttackRule::clone() const +{ + return NEW MTGPlaneswalkerAttackRule(*this); +} + +bool MTGPlaneswalkerAttackRule::select(Target* t) +{ + if (CardView* c = dynamic_cast(t)) + { + MTGCardInstance * card = c->getCard(); + if (card->canAttack() && !card->isPhased) + return true; + } + return false; +} +bool MTGPlaneswalkerAttackRule::greyout(Target* t) +{ + return true; +} + +//setting combat against planeswalker menu handling + AAPlaneswalkerAttacked::AAPlaneswalkerAttacked(GameObserver* observer, int id, MTGCardInstance * card, MTGCardInstance * _target): + InstantAbility(observer, id, source) +{ + this->target = _target; + menuText = _target->name.c_str(); + attacker = card; +} + +int AAPlaneswalkerAttacked::resolve() +{ + if(!attacker) + return 0; + attacker->isAttacking = this->target; + attacker->toggleAttacker(); + return 1; +} + +const char* AAPlaneswalkerAttacked::getMenuText() +{ + return menuText.c_str(); +} + +AAPlaneswalkerAttacked * AAPlaneswalkerAttacked::clone() const +{ + return NEW AAPlaneswalkerAttacked(*this); +} + +AAPlaneswalkerAttacked::~AAPlaneswalkerAttacked() +{ +} //this rules handles returning cards to combat triggers for activations. MTGCombatTriggersRule::MTGCombatTriggersRule(GameObserver* observer, int _id) : @@ -2159,6 +2274,49 @@ MTGPlaneWalkerRule * MTGPlaneWalkerRule::clone() const { return NEW MTGPlaneWalkerRule(*this); } +/* planeswalker damage rule */ +MTGPlaneswalkerDamage::MTGPlaneswalkerDamage(GameObserver* observer, int _id) : +PermanentAbility(observer, _id) +{ +} +; + +int MTGPlaneswalkerDamage::receiveEvent(WEvent * event) +{ + + if (event->type == WEvent::DAMAGE) + { + WEventDamage * e = (WEventDamage *) event; + Damage * d = e->damage; + MTGCardInstance * card = dynamic_cast(e->getTarget(WEvent::TARGET_TO)); + if (d->damage > 0 && card && card->hasType(Subtypes::TYPE_PLANESWALKER)) + { + int howMany = d->damage; + for(int k = 0;k < howMany;k++) + { + card->counters->removeCounter("loyalty",0,0); + } + d->damage = 0; + return 1; + } + } + if (WEventCounters * removel = dynamic_cast(event)) + { + if(removel->removed && removel->targetCard && removel->targetCard->hasType(Subtypes::TYPE_PLANESWALKER)) + if(!removel->targetCard->counters->hasCounter("loyalty",0,0)) + { + removel->targetCard->bury(); + return 1; + } + + } + return 0; +} + +MTGPlaneswalkerDamage * MTGPlaneswalkerDamage::clone() const +{ + return NEW MTGPlaneswalkerDamage(*this); +} /* Lifelink */ MTGLifelinkRule::MTGLifelinkRule(GameObserver* observer, int _id) : diff --git a/projects/mtg/src/ManaCost.cpp b/projects/mtg/src/ManaCost.cpp index f7f894c84..cb6d7fac9 100644 --- a/projects/mtg/src/ManaCost.cpp +++ b/projects/mtg/src/ManaCost.cpp @@ -165,7 +165,11 @@ ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstan } case 'c': //Counters or cycle { - if(value == "cycle") + if(value == "chosencolor") + { + manaCost->add(c->chooseacolor, 1); + } + else if(value == "cycle") { manaCost->addExtraCost(NEW CycleCost(tc)); } @@ -631,6 +635,11 @@ int ManaCost::addExtraCost(ExtraCost * _cost) int ManaCost::addExtraCosts(ExtraCosts *_ecost) { + if(!_ecost) + { + extraCosts = NULL; + return 1; + } if (!extraCosts) extraCosts = NEW ExtraCosts(); for(size_t i = 0; i < _ecost->costs.size(); i++) diff --git a/projects/mtg/src/Rules.cpp b/projects/mtg/src/Rules.cpp index 11e479114..8d0494084 100644 --- a/projects/mtg/src/Rules.cpp +++ b/projects/mtg/src/Rules.cpp @@ -140,6 +140,7 @@ void RulesState::parsePlayerState(int playerId, string s) void Rules::addExtraRules(GameObserver* g) { int id = g->mLayers->actionLayer()->getMaxId(); + MTGAllCards::sortSubtypeList(); for (int i = 0; i < 2; ++i) { Player * p = g->players[i]; diff --git a/projects/mtg/src/TargetChooser.cpp b/projects/mtg/src/TargetChooser.cpp index 36893dd0d..455be856a 100644 --- a/projects/mtg/src/TargetChooser.cpp +++ b/projects/mtg/src/TargetChooser.cpp @@ -574,6 +574,29 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta cd->setColor(cid); } } + + if (attribute.find("chosencolor") != string::npos) + { + attributefound = 1; + if (minus) + cd->SetExclusionColor(card->chooseacolor); + else + cd->setColor(card->chooseacolor); + } + + if (attribute.find("chosentype") != string::npos) + { + attributefound = 1; + if (minus) + { + cd->setNegativeSubtype(card->chooseasubtype); + } + else + { + cd->setSubtype(card->chooseasubtype); + } + } + if (!attributefound) { //Abilities