diff --git a/projects/mtg/bin/Res/sets/ONS/_cards.dat b/projects/mtg/bin/Res/sets/ONS/_cards.dat index eb3ff5b93..5acf107a3 100644 --- a/projects/mtg/bin/Res/sets/ONS/_cards.dat +++ b/projects/mtg/bin/Res/sets/ONS/_cards.dat @@ -8,6 +8,16 @@ type=Sorcery auto=foreach(bird|myBattlefield) draw:1 [/card] [card] +id=41168 +name=Akroma's Vengeance +mana={4}{W}{W} +type=Sorcery +autohand={3}:cycling +auto=destroy all(artifact,creature,enchantment) +text=Destroy all artifacts, creatures, and enchantments. Cycling {3} ({3}, Discard this card: Draw a card.) +rarity=R +[/card] +[card] text=Enchant land (Target a land as you play this. This card enters the battlefield attached to that land.) You control enchanted land. id=41463 alias=1194 diff --git a/projects/mtg/bin/Res/sets/ONS/todo.dat b/projects/mtg/bin/Res/sets/ONS/todo.dat index a644dc528..6f662f84c 100644 --- a/projects/mtg/bin/Res/sets/ONS/todo.dat +++ b/projects/mtg/bin/Res/sets/ONS/todo.dat @@ -47,14 +47,6 @@ text=Creatures you control gain protection from the color of your choice until e rarity=U [/card] [card] -id=41168 -name=Akroma's Vengeance -mana={4}{W}{W} -type=Sorcery -text=Destroy all artifacts, creatures, and enchantments. Cycling {3} ({3}, Discard this card: Draw a card.) -rarity=R -[/card] -[card] id=39882 name=Ancestor's Prophet mana={4}{W} diff --git a/projects/mtg/bin/Res/test/_tests.txt b/projects/mtg/bin/Res/test/_tests.txt index fe8228e62..a2036cc3e 100644 --- a/projects/mtg/bin/Res/test/_tests.txt +++ b/projects/mtg/bin/Res/test/_tests.txt @@ -2,15 +2,13 @@ #Generic engine features ######################## generic/attacks_each_turn.txt +generic/cycling.txt generic/deathtouch.txt generic/doesnotuntap.txt generic/doesnotuntap2.txt generic/double_strike.txt generic/equip_landfall_buff.txt generic/equip_reach.txt -generic/equip_shroud.txt -generic/equip_shroud2.txt -generic/equip_wither.txt generic/fear.txt generic/fear_i147.txt generic/first_and_double_strike1_i187.txt diff --git a/projects/mtg/bin/Res/test/generic/cycling.txt b/projects/mtg/bin/Res/test/generic/cycling.txt new file mode 100644 index 000000000..735b5521b --- /dev/null +++ b/projects/mtg/bin/Res/test/generic/cycling.txt @@ -0,0 +1,17 @@ +#Testing Cycling +[INIT] +FIRSTMAIN +[PLAYER1] +hand:Akroma's Vengeance +library:forest +manapool:{3} +[PLAYER2] +[DO] +Akroma's Vengeance +[ASSERT] +FIRSTMAIN +[PLAYER1] +graveyard:Akroma's Vengeance +hand:forest +[PLAYER2] +[END] \ No newline at end of file diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index 64b27e40f..d2fd2a50d 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -383,7 +383,8 @@ class GenericActivatedAbility:public ActivatedAbility{ MTGAbility * ability; int limitPerTurn; int counters; - GenericActivatedAbility(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int _tap = 0, int limit = 0, int myTurnOnly = 0):ActivatedAbility(_id, card,_cost,myTurnOnly,_tap),ability(a),limitPerTurn(limit){ + MTGGameZone * activeZone; + GenericActivatedAbility(int _id, MTGCardInstance * card, MTGAbility * a, ManaCost * _cost, int _tap = 0, int limit = 0, int myTurnOnly = 0, MTGGameZone * dest = NULL):ActivatedAbility(_id, card,_cost,myTurnOnly,_tap),ability(a),limitPerTurn(limit),activeZone(dest){ counters = 0; target = ability->target; } @@ -424,6 +425,13 @@ class GenericActivatedAbility:public ActivatedAbility{ } } + int testDestroy(){ + if (!activeZone) return ActivatedAbility::testDestroy(); + if (activeZone->hasCard(source)) return 0; + return 1; + + } + }; /* Generic TargetAbility */ @@ -432,7 +440,8 @@ class GenericTargetAbility:public TargetAbility{ public: int limitPerTurn; int counters; - GenericTargetAbility(int _id, MTGCardInstance * _source, TargetChooser * _tc,MTGAbility * a, ManaCost * _cost = NULL, int _tap=0, int limit = 0, int myTurnOnly = 0):TargetAbility(_id,_source, _tc,_cost,myTurnOnly,_tap),limitPerTurn(limit){ + MTGGameZone * activeZone; + GenericTargetAbility(int _id, MTGCardInstance * _source, TargetChooser * _tc,MTGAbility * a, ManaCost * _cost = NULL, int _tap=0, int limit = 0, int myTurnOnly = 0, MTGGameZone * dest = NULL):TargetAbility(_id,_source, _tc,_cost,myTurnOnly,_tap),limitPerTurn(limit), activeZone(dest){ ability = a; counters = 0; } @@ -460,10 +469,41 @@ public: TargetAbility::Update(dt); } + int testDestroy(){ + if (!activeZone) return TargetAbility::testDestroy(); + if (activeZone->hasCard(source)) return 0; + return 1; + + } }; +//Cycling +class ACycle:public ActivatedAbility{ + public: + ACycle(int _id, MTGCardInstance * card,Targetable * _target):ActivatedAbility(_id, card){ + target = _target; + } + + int resolve(){ + source->controller()->game->putInGraveyard(source); + source->controller()->game->drawFromLibrary(); + return 1; + } + + const char * getMenuText(){ + return "Cycling"; + } + + ACycle * clone() const{ + ACycle * a = NEW ACycle(*this); + a->isClone = 1; + return a; + } + + +}; //Drawer, allows to draw a card for a cost: diff --git a/projects/mtg/include/MTGAbility.h b/projects/mtg/include/MTGAbility.h index 080cd5323..4e5dc7012 100644 --- a/projects/mtg/include/MTGAbility.h +++ b/projects/mtg/include/MTGAbility.h @@ -203,10 +203,10 @@ class AbilityFactory{ int parsePowerToughness(string s, int *power, int *toughness); TriggeredAbility * parseTrigger(string s, int id, Spell * spell, MTGCardInstance *card, Targetable * target); public: - int getAbilities(vector * v, Spell * spell, MTGCardInstance * card = NULL, int id = 0); - MTGAbility * parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated = 0, int forceUEOT = 0); + int getAbilities(vector * v, Spell * spell, MTGCardInstance * card = NULL, int id = 0,MTGGameZone * dest = NULL); + MTGAbility * parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated = 0, int forceUEOT = 0,MTGGameZone * dest = NULL); int abilityEfficiency(MTGAbility * a, Player * p, int mode = MODE_ABILITY, TargetChooser * tc = NULL); - int magicText(int id, Spell * spell, MTGCardInstance * card = NULL, int mode = MODE_PUTINTOPLAY, TargetChooser * tc = NULL); + int magicText(int id, Spell * spell, MTGCardInstance * card = NULL, int mode = MODE_PUTINTOPLAY, TargetChooser * tc = NULL, MTGGameZone * dest = NULL); static int computeX(Spell * spell, MTGCardInstance * card); int destroyAllInPlay(TargetChooser * tc, int bury = 0); int moveAll(TargetChooser * tc, string destinationZone); diff --git a/projects/mtg/include/MTGCard.h b/projects/mtg/include/MTGCard.h index 7192d741d..32faee9e6 100644 --- a/projects/mtg/include/MTGCard.h +++ b/projects/mtg/include/MTGCard.h @@ -36,6 +36,7 @@ class MTGCard { int colors[Constants::MTG_NB_COLORS]; map basicAbilities; + map magicTexts; string magicText; int alias; string spellTargetType; @@ -71,6 +72,7 @@ class MTGCard { const char * getText(); void addMagicText(string value); + void addMagicText(string value, string zone); void setName(string value); const string getName() const; diff --git a/projects/mtg/include/MTGRules.h b/projects/mtg/include/MTGRules.h index f5493420e..319f9a85a 100644 --- a/projects/mtg/include/MTGRules.h +++ b/projects/mtg/include/MTGRules.h @@ -8,6 +8,14 @@ #include "../include/Counters.h" #include "../include/WEvent.h" +class OtherAbilitiesEventReceiver:public MTGAbility{ +public: + int testDestroy(); + int receiveEvent(WEvent * event); + OtherAbilitiesEventReceiver(int _id); + OtherAbilitiesEventReceiver * clone() const; +}; + class MTGPutInPlayRule:public MTGAbility{ public: int isReactingToClick(MTGCardInstance * card, ManaCost * mana = NULL); diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index 7a0321ad9..647e20dc2 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -214,7 +214,7 @@ int AIAction::getEfficiency(){ } break; } - if (p->game->hand->nb_cards == 0) efficiency *= 1.3; //increase chance of using ability if hand is empty + if (p->game->hand->nb_cards == 0) efficiency = (int) ((float) efficiency * 1.3); //increase chance of using ability if hand is empty return efficiency; } diff --git a/projects/mtg/src/AIStats.cpp b/projects/mtg/src/AIStats.cpp index 4dbbebec9..e02da4a1c 100644 --- a/projects/mtg/src/AIStats.cpp +++ b/projects/mtg/src/AIStats.cpp @@ -163,7 +163,7 @@ void AIStats::Render(){ MTGCard * card = GameApp::collection->getCardById(stat->source); if (card) { sprintf(buffer, "%s %i", card->getName().c_str(), stat->value); - f->DrawString(buffer,x0+5,10 + 16 *i); + f->DrawString(buffer,x0+5,10 + 16 *(float)i); i++; } } diff --git a/projects/mtg/src/DuelLayers.cpp b/projects/mtg/src/DuelLayers.cpp index dee277b98..1cda5b9a1 100644 --- a/projects/mtg/src/DuelLayers.cpp +++ b/projects/mtg/src/DuelLayers.cpp @@ -27,6 +27,7 @@ void DuelLayers::init(){ action->Add(NEW MTGPersistRule(-1)); action->Add(NEW MTGLifelinkRule(-1)); action->Add(NEW MTGDeathtouchRule(-1)); + action->Add(NEW OtherAbilitiesEventReceiver(-1)); //Other display elements action->Add(NEW HUDDisplay(-1)); diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 1299c1fd7..d80f968e1 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -145,12 +145,10 @@ TriggeredAbility * AbilityFactory::parseTrigger(string magicText, int id, Spell - - //Parses a string and returns the corresponding MTGAbility object // Returns NULL if parsing failed //Beware, Spell CAN be null when the function is called by the AI trying to analyze the effects of a given card -MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated, int forceUEOT){ +MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTGCardInstance *card, int activated, int forceUEOT, MTGGameZone * dest){ size_t found; string whitespaces (" \t\f\v\n\r"); @@ -240,8 +238,8 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG tc = tcf.createTargetChooser(starget, card); } - if (tc) return NEW GenericTargetAbility(id, card, tc, a,cost, doTap,limit,myTurnOnly); - return NEW GenericActivatedAbility(id, card, a,cost,doTap,limit,myTurnOnly); + if (tc) return NEW GenericTargetAbility(id, card, tc, a,cost, doTap,limit,myTurnOnly,dest); + return NEW GenericActivatedAbility(id, card, a,cost,doTap,limit,myTurnOnly,dest); } SAFE_DELETE(cost); } @@ -362,6 +360,13 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG return NULL; } + //Cycling + found = s.find("cycling"); + if (found != string::npos){ + MTGAbility * a = NEW ACycle(id,card,target); + a->oneShot = 1; + return a; + } //Fizzle (counterspell...) found = s.find("fizzle"); @@ -842,14 +847,32 @@ int AbilityFactory::computeX(Spell * spell, MTGCardInstance * card){ } -int AbilityFactory::getAbilities(vector * v, Spell * spell, MTGCardInstance * card, int id){ +int AbilityFactory::getAbilities(vector * v, Spell * spell, MTGCardInstance * card, int id, MTGGameZone * dest){ if (!card && spell) card = spell->source; if (!card) return 0; MTGCardInstance * target = card->target; if (!target) target = card; - string magicText = card->magicText; - if (card->alias && magicText.size() == 0){ + string magicText; + if (dest) { + GameObserver * g = GameObserver::GetInstance(); + for (int i = 0; i < 2 ; ++i){ + MTGPlayerCards * zones = g->players[i]->game; + if (dest == zones->hand){ + magicText = card->magicTexts["hand"]; + break; + } + if (dest == zones->graveyard){ + magicText = card->magicTexts["graveyard"]; + break; + } + //Other zones needed ? + return 0; + } + }else{ + magicText = card->magicText; + } + if (card->alias && magicText.size() == 0 && !dest){ MTGCard * c = GameApp::collection->getCardById(card->alias); if (!c) return 0; magicText = c->magicText; @@ -870,7 +893,7 @@ int AbilityFactory::getAbilities(vector * v, Spell * spell, MTGCar magicText = ""; } - MTGAbility * a = parseMagicLine(line, result, spell, card); + MTGAbility * a = parseMagicLine(line, result, spell, card,0,0,dest); if (a){ v->push_back(a); result++; @@ -890,12 +913,12 @@ int AbilityFactory::getAbilities(vector * v, Spell * spell, MTGCar * - target (if there ie a "target(" in the string, then this is a TargetAbility) * - doTap (a dirty way to know if tapping is included in the cost... */ -int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int mode, TargetChooser * tc){ +int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int mode, TargetChooser * tc,MTGGameZone * dest){ int dryMode = 0; - if (!spell) dryMode = 1; + if (!spell && !dest) dryMode = 1; vector v; - int result = getAbilities(&v,spell,card,id); + int result = getAbilities(&v,spell,card,id,dest); for (size_t i = 0; i < v.size(); ++i){ MTGAbility * a = v[i]; diff --git a/projects/mtg/src/MTGCard.cpp b/projects/mtg/src/MTGCard.cpp index 3e3511036..de6ed2557 100644 --- a/projects/mtg/src/MTGCard.cpp +++ b/projects/mtg/src/MTGCard.cpp @@ -46,6 +46,9 @@ MTGCard::MTGCard(MTGCard * source){ mtgid = source->mtgid; setId = source->setId; magicText = source->magicText; + for(map::const_iterator it = source->magicTexts.begin(); it != source->magicTexts.end(); ++it){ + magicTexts[it->first] = source->magicTexts[it->first]; + } spellTargetType = source->spellTargetType; alias = source->alias; } @@ -63,6 +66,7 @@ int MTGCard::init(){ setId = 0; mtgid = 0; magicText = ""; + magicTexts.clear(); spellTargetType = ""; alias = 0; rarity = Constants::RARITY_C; @@ -260,6 +264,12 @@ void MTGCard::addMagicText(string value){ magicText.append(value); } +void MTGCard::addMagicText(string value, string key){ + std::transform( value.begin(), value.end(), value.begin(),::tolower ); + if (magicTexts[key].size()) magicTexts[key].append("\n"); + magicTexts[key].append(value); +} + void MTGCard::setName( string value){ name = value; lcname = value; diff --git a/projects/mtg/src/MTGDeck.cpp b/projects/mtg/src/MTGDeck.cpp index 2ab4ff486..f29e31b9c 100644 --- a/projects/mtg/src/MTGDeck.cpp +++ b/projects/mtg/src/MTGDeck.cpp @@ -30,6 +30,9 @@ int MTGAllCards::processConfLine(string s, MTGCard *card){ if(key.compare( "auto")==0){ card->addMagicText(value); } + else if(key.find("auto") == 0){ + card->addMagicText(value,key.substr(4)); + } else if(key.compare( "alias")==0){ card->alias=atoi(value.c_str()); } diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index 0139c9576..432c6cecb 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -164,6 +164,36 @@ ostream& MTGAttackRule::toString(ostream& out) const } +OtherAbilitiesEventReceiver::OtherAbilitiesEventReceiver(int _id):MTGAbility(_id,NULL){ +} + + +int OtherAbilitiesEventReceiver::receiveEvent(WEvent *e){ + if (WEventZoneChange* event = dynamic_cast(e)) { + if (event->to && (event->to != event->from)){ + GameObserver * g = GameObserver::GetInstance(); + for (int i = 0; i < 2; ++i){ + if (event->to == g->players[i]->game->inPlay) return 0; + } + AbilityFactory af; + af.magicText(g->mLayers->actionLayer()->getMaxId(), NULL, event->card, 1, 0,event->to); + return 1; + } + } + return 0; +} + +int OtherAbilitiesEventReceiver::testDestroy(){ + return 0; +} + + OtherAbilitiesEventReceiver * OtherAbilitiesEventReceiver::clone() const{ + OtherAbilitiesEventReceiver * a = NEW OtherAbilitiesEventReceiver(*this); + a->isClone = 1; + return a; + } + + MTGBlockRule::MTGBlockRule(int _id):MTGAbility(_id,NULL){ aType=MTGAbility::MTG_BLOCK_RULE; }