diff --git a/projects/mtg/include/AIPlayer.h b/projects/mtg/include/AIPlayer.h index 93401813c..600d7fc9e 100644 --- a/projects/mtg/include/AIPlayer.h +++ b/projects/mtg/include/AIPlayer.h @@ -14,6 +14,9 @@ using std::queue; #define INFO_NBCREATURES 0 #define INFO_CREATURESPOWER 1 +#define INFO_CREATURESRANK 2 +#define INFO_CREATURESTOUGHNESS 3 +#define INFO_CREATURESATTACKINGPOWER 4 class AIStats; @@ -27,6 +30,7 @@ public: NestedAbility * nability; Player * player; int id; + bool checked; MTGCardInstance * click; MTGCardInstance * target; // TODO Improve AIAction(MTGAbility * a, MTGCardInstance * c, MTGCardInstance * t = NULL):ability(a),click(c),target(t){player = NULL; efficiency = -1; id = currentId++;}; @@ -51,7 +55,6 @@ class CmpAbilities { // compares Abilities efficiency class AIPlayer: public Player{ protected: MTGCardInstance * nextCardToPlay; - int agressivity; queue clickstream; void tapLandsForMana(ManaCost * cost, MTGCardInstance * card = NULL); int orderBlockers(); @@ -65,6 +68,8 @@ class AIPlayer: public Player{ AIStats * getStats(); //Variables used by Test suite public: + int agressivity; + bool Checked; bool forceBestAbilityUse; void End(){}; virtual int displayStack() {return 0;}; diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index d879a84cf..1e85fce47 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -20,7 +20,6 @@ #include #include - #include using std::map; @@ -1078,6 +1077,8 @@ class AADrawer:public ActivatedAbilityTP{ public: WParsedInt *nbcards; AADrawer(int _id, MTGCardInstance * card,Targetable * _target,ManaCost * _cost, WParsedInt * _nbcards, int _tap = 0, int who=TargetChooser::UNSET):ActivatedAbilityTP(_id, card,_target,_cost,_tap,who),nbcards(_nbcards){ + aType = MTGAbility::STANDARD_DRAW; + nbcardAmount = nbcards->getValue(); } int resolve(){ @@ -1647,78 +1648,6 @@ public: } }; -//equipment -class AEquip:public TargetAbility{ -public: - vector currentAbilities; - AEquip(int _id, MTGCardInstance * _source, ManaCost * _cost=NULL, int doTap=0, int restrictions = ActivatedAbility::AS_SORCERY):TargetAbility(_id, _source,NULL,_cost,restrictions,doTap){ - aType = MTGAbility::STANDARD_EQUIP; - } - - int unequip(){ - if(source->target){source->target->equipment -= 1;} - source->target = NULL; - for (size_t i = 0; i < currentAbilities.size(); ++i){ - MTGAbility * a = currentAbilities[i]; - if(dynamic_cast(a)){ - SAFE_DELETE(a); - continue; - } - GameObserver::GetInstance()->removeObserver(currentAbilities[i]); - } - currentAbilities.clear(); - return 1; - } - - - int equip(MTGCardInstance * equipped){ - source->target = equipped; - source->target->equipment += 1; - AbilityFactory af; - af.getAbilities(¤tAbilities,NULL,source); - for (size_t i = 0; i < currentAbilities.size(); ++i){ - MTGAbility * a = currentAbilities[i]; - if(dynamic_cast(a)) continue; - a->addToGame(); - } - return 1; - - } - - - int resolve(){ - MTGCardInstance * mTarget = tc->getNextCardTarget(); - if (!mTarget) return 0; - if (mTarget == source) return 0; - unequip(); - equip(mTarget); - return 1; - } - - const char * getMenuText(){ - return "Equip"; - } - - - int testDestroy(){ - if (source->target && !game->isInPlay(source->target)) - unequip(); - return TargetAbility::testDestroy(); - } - - int destroy(){ - unequip(); - return TargetAbility::destroy(); - } - - AEquip * clone() const{ - AEquip * a = NEW AEquip(*this); - a->isClone = 1; - return a; - } - -}; - /*Gives life each time a spell matching CardDescriptor's criteria are match . Optionnal manacost*/ class ASpellCastLife:public MTGAbility{ public: @@ -1890,7 +1819,7 @@ class APowerToughnessModifier: public MTGAbility{ public: WParsedPT * wppt; APowerToughnessModifier(int id, MTGCardInstance * _source, MTGCardInstance * _target, WParsedPT * wppt):MTGAbility(id,_source,_target),wppt(wppt){ - + aType = MTGAbility::STANDARD_PUMP; } int addToGame(){ @@ -1925,6 +1854,7 @@ class AInstantPowerToughnessModifierUntilEOT: public InstantAbility{ public: WParsedPT * wppt; AInstantPowerToughnessModifierUntilEOT(int _id, MTGCardInstance * _source, MTGCardInstance * _target, WParsedPT * wppt): InstantAbility(_id, _source, _target), wppt(wppt){ + aType = MTGAbility::STANDARD_PUMP; } int resolve(){ @@ -1969,6 +1899,7 @@ public: counters = 0; target=_target; ability = NEW AInstantPowerToughnessModifierUntilEOT(id,_source,_target,wppt); + aType = MTGAbility::STANDARD_PUMP; } int isReactingToClick(MTGCardInstance * card, ManaCost * cost = NULL){ @@ -2446,8 +2377,155 @@ public: return a; } }; +//////////////////////////////////////////////////////////////////////////////////////////////////// +//a different lord for auras and enchantments. http://code.google.com/p/wagic/issues/detail?id=244 +class ATeach:public ListMaintainerAbility, public NestedAbility{ +public: + int includeSelf; + map skills; + + ATeach(int _id, MTGCardInstance * card, TargetChooser * _tc, int _includeSelf, MTGAbility * a):ListMaintainerAbility(_id,card), NestedAbility(a){ + tc = _tc; + tc->targetter = NULL; + includeSelf = NULL; + } + + int canBeInList(MTGCardInstance * card){ + if ((tc->source->hasSubtype("aura") || tc->source->hasSubtype("equipment") || tc->source->hasSubtype("instant") || tc->source->hasSubtype("sorcery"))&& tc->canTarget(card) && card == tc->source->target && card != tc->source) return 1; + return 0; + } + + int resolve(){ + updateTargets(); + cards.clear(); + players.clear(); + return 1; + } + + int added(MTGCardInstance * card){ + return _added(card); + } + + int removed(MTGCardInstance * card){ + if(skills.find(card) != skills.end()){ + game->removeObserver(skills[card]); + skills.erase(card); + } + return 1; + } + + int _added(Damageable * d){ + MTGAbility * a = ability->clone(); + + if(a->source->hasSubtype("aura") || a->source->hasSubtype("equipment") || a->source->hasSubtype("instant") || a->source->hasSubtype("sorcery")) + { + a->target = a->source->target; + }else{ + return 0; + } + + if (a->oneShot){ + a->resolve(); + delete(a); + }else{ + if (d->type_as_damageable == DAMAGEABLE_MTGCARDINSTANCE){ + a->source = (MTGCardInstance *)d; + } + if (oneShot){ + MTGAbility * wrapper = NEW GenericInstantAbility(1,source,d,a); + wrapper->addToGame(); + }else{ + skills[d] = a; + a->addToGame(); + } + } + return 1; + } + ~ATeach(){ + if (!isClone) SAFE_DELETE(ability); + } + + ATeach * clone() const{ + ATeach * a = NEW ATeach(*this); + a->isClone = 1; + return a; + } + +}; +// + +//equipment +class AEquip:public TargetAbility{ +public: + vector currentAbilities; + AEquip(int _id, MTGCardInstance * _source, ManaCost * _cost=NULL, int doTap=0, int restrictions = ActivatedAbility::AS_SORCERY):TargetAbility(_id,_source,NULL,_cost,restrictions,doTap){ + aType = MTGAbility::STANDARD_EQUIP; + } + + int unequip(){ + if(source->target){source->target->equipment -= 1;} + source->target = NULL; + for (size_t i = 0; i < currentAbilities.size(); ++i){ + MTGAbility * a = currentAbilities[i]; + if(dynamic_cast(a)){ + SAFE_DELETE(a); + continue; + } + GameObserver::GetInstance()->removeObserver(currentAbilities[i]); + } + currentAbilities.clear(); + return 1; + } + int equip(MTGCardInstance * equipped){ + source->target = equipped; + source->target->equipment += 1; + AbilityFactory af; + af.getAbilities(¤tAbilities,NULL,source); + for (size_t i = 0; i < currentAbilities.size(); ++i){ + MTGAbility * a = currentAbilities[i]; + if(dynamic_cast(a)) continue; + if(dynamic_cast(a)) continue; + a->addToGame(); + } + return 1; + + } + + + int resolve(){ + MTGCardInstance * mTarget = tc->getNextCardTarget(); + if (!mTarget) return 0; + if (mTarget == source) return 0; + unequip(); + equip(mTarget); + return 1; + } + + const char * getMenuText(){ + return "Equip"; + } + + + int testDestroy(){ + if (source->target && !game->isInPlay(source->target)) + unequip(); + return TargetAbility::testDestroy(); + } + + int destroy(){ + unequip(); + return TargetAbility::destroy(); + } + + AEquip * clone() const{ + AEquip * a = NEW AEquip(*this); + a->isClone = 1; + return a; + } + +}; //Foreach (plague rats...) class AForeach:public ListMaintainerAbility, public NestedAbility{ public: @@ -3323,6 +3401,7 @@ public: listtypes; listcolors; AForeverTransformer(int id, MTGCardInstance * source, MTGCardInstance * target, string stypes, string sabilities):MTGAbility(id,source,target){ + aType = MTGAbility::STANDARD_BECOMES; //TODO this is a copy/past of other code that's all around the place, everything should be in a dedicated parser class; MTGCardInstance * _target = (MTGCardInstance *)target; for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++){ @@ -3376,7 +3455,9 @@ class ATransformerUEOT: public InstantAbility{ public: ATransformer * ability; ATransformerUEOT(int id, MTGCardInstance * source, MTGCardInstance * target, string types, string abilities):InstantAbility(id,source,target){ - ability = NEW ATransformer(id,source,target,types,abilities);} + ability = NEW ATransformer(id,source,target,types,abilities); + aType = MTGAbility::STANDARD_BECOMES; + } int resolve(){ ATransformer * a = ability->clone(); GenericInstantAbility * wrapper = NEW GenericInstantAbility(1,source,(Damageable *)(this->target),a); @@ -3399,6 +3480,7 @@ public: AForeverTransformer * ability; ATransformerFOREVER(int id, MTGCardInstance * source, MTGCardInstance * target, string types, string abilities):InstantAbility(id,source,target){ ability = NEW AForeverTransformer(id,source,target,types,abilities); + aType = MTGAbility::STANDARD_BECOMES; } int resolve(){ AForeverTransformer * a = ability->clone(); @@ -3450,6 +3532,7 @@ public: WParsedPT * wppt; string menu; ABecomes(int id, MTGCardInstance * source, MTGCardInstance * target, string stypes, WParsedPT * wppt, string sabilities):MTGAbility(id,source,target),wppt(wppt){ + aType = MTGAbility::STANDARD_BECOMES; //TODO this is a copy/past of other code that's all around the place, everything should be in a dedicated parser class; for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++){ @@ -3543,6 +3626,7 @@ public: ABecomes * ability; ABecomesUEOT(int id, MTGCardInstance * source, MTGCardInstance * target, string types, WParsedPT * wpt, string abilities):InstantAbility(id,source,target){ ability = NEW ABecomes(id,source,target,types,wpt,abilities); + aType = MTGAbility::STANDARD_BECOMES; } int resolve(){ diff --git a/projects/mtg/include/DeckManager.h b/projects/mtg/include/DeckManager.h index ef00ea99b..b03fcee5b 100644 --- a/projects/mtg/include/DeckManager.h +++ b/projects/mtg/include/DeckManager.h @@ -1,5 +1,6 @@ #include #include + #include "DeckMetaData.h" using namespace std; @@ -28,6 +29,11 @@ public: static DeckManager * GetInstance(); static void EndInstance(); + //convenience method to get the difficulty rating between two decks. This should be refined a little more + //since the eventual move of all deck meta data should be managed by this class + + static int getDifficultyRating( Player *statsPlayer, Player *player ); + ~DeckManager() { diff --git a/projects/mtg/include/MTGAbility.h b/projects/mtg/include/MTGAbility.h index 5aa8ecb53..a00cdd516 100644 --- a/projects/mtg/include/MTGAbility.h +++ b/projects/mtg/include/MTGAbility.h @@ -57,6 +57,7 @@ class MTGAbility: public ActionElement{ Targetable * target; int aType; int naType; + int nbcardAmount; MTGCardInstance * source; MTGAbility(int id, MTGCardInstance * card); MTGAbility(int id, MTGCardInstance * _source, Targetable * _target); @@ -95,6 +96,9 @@ class MTGAbility: public ActionElement{ STANDARD_EQUIP = 14, STANDARD_LEVELUP = 15, FOREACH = 16, + STANDARD_DRAW = 17, + STANDARD_PUMP = 18, + STANDARD_BECOMES = 19, }; diff --git a/projects/mtg/include/MTGGameZones.h b/projects/mtg/include/MTGGameZones.h index 97cbe30d8..8aca04c9a 100644 --- a/projects/mtg/include/MTGGameZones.h +++ b/projects/mtg/include/MTGGameZones.h @@ -154,6 +154,7 @@ class MTGPlayerCards { MTGPlayerCards(MTGDeck * deck); ~MTGPlayerCards(); void initGame(int shuffle = 1, int draw = 1); + void OptimizedHand(int amount = 7,int lands = 3,int creatures = 0,int othercards = 4); void setOwner(Player * player); void discardRandom(MTGGameZone * from,MTGCardInstance * source); void drawFromLibrary(); diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index 02da40e47..f518be71d 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -35,6 +35,7 @@ AIPlayer::AIPlayer(MTGDeck * deck, string file, string fileSmall) : Player(deck, stats = NULL; agressivity = 50; forceBestAbilityUse = false; + Checked = false; playMode = Player::MODE_AI; } @@ -122,7 +123,6 @@ ManaCost * AIPlayer::getPotentialMana(MTGCardInstance * target){ } } } - return result; } @@ -248,11 +248,42 @@ int AIAction::getEfficiency(){ } if (currentlevel < _target->MaxLevelUp){ efficiency = 85; - efficiency += currentlevel;//increase the efficeincy of leveling up by a small amount equal to current level. +//increase the efficeincy of leveling up by a small amount equal to current level. + efficiency += currentlevel; } break; } - + case MTGAbility::STANDARD_PUMP:{ + MTGCardInstance * _target = (MTGCardInstance *)(a->target); +//i do not set a starting eff. on this ability, this allows Ai to sometimes randomly do it as it normally does. + if(g->getCurrentGamePhase() == Constants::MTG_PHASE_COMBATBLOCKERS) + { + if(_target && BAKA_EFFECT_GOOD) + { + if((_target->defenser || _target->blockers.size()) + && ((_target->power < _target->getNextOpponent()->toughness || _target->toughness < _target->getNextOpponent()->power) + || (_target->has(Constants::TRAMPLE)))){ +//this pump is based on a start eff. of 20 multiplied by how good the creature is. + efficiency = 20*_target->DangerRanking(); + } + if(_target->isAttacker() && !_target->blockers.size()){ +//this means im heading directly for the player, pump this creature as much as possible. + efficiency = 100; + } + } + } + break; + } + case MTGAbility::STANDARD_BECOMES: + { + MTGCardInstance * _target = (MTGCardInstance *)(a->target); +//nothing huge here, just ensuring that Ai makes his noncreature becomers into creatures during first main, so it can actually use them in combat. + if(_target && !_target->hasType("Creature") && g->getCurrentGamePhase() == Constants::MTG_PHASE_FIRSTMAIN) + { + efficiency = 100; + } + break; + } case MTGAbility::MANA_PRODUCER: //can't use mana producers right now :/ efficiency = 0; break; @@ -684,7 +715,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty if (card->hasType(Subtypes::TYPE_CREATURE) && this->castrestrictedcreature < 0 && this->castrestrictedspell < 0) continue; if (card->hasType(Subtypes::TYPE_ENCHANTMENT) && this->castrestrictedspell < 0) continue; if (card->hasType(Subtypes::TYPE_ARTIFACT) && this->castrestrictedspell < 0) continue; - if (card->hasType(Subtypes::TYPE_SORCERY) && this->castrestrictedspell < 0) continue; + if (card->hasType(Subtypes::TYPE_SORCERY) && this->castrestrictedspell < 0) continue; if (card->hasType(Subtypes::TYPE_INSTANT) && this->castrestrictedspell < 0) continue; if (card->hasType(Subtypes::TYPE_LAND) && !this->canPutLandsIntoPlay) continue; if (card->hasType(Subtypes::TYPE_LEGENDARY) && game->inPlay->findByName(card->name)) continue; @@ -792,18 +823,50 @@ int AIPlayerBaka::computeActions(){ if (potential) delete(currentMana); if (nextCardToPlay){ if (potential){ - tapLandsForMana(nextCardToPlay->getManaCost()); - } + ///////////////////////// + //had to force this on Ai other wise it would pay nothing but 1 color for a sunburst card. + //this does not teach it to use manaproducer more effectively, it simply allow it to use the manaproducers it does understand better on sunburst by force. + if(nextCardToPlay->has(Constants::SUNBURST)){ + ManaCost * SunCheck = manaPool; + SunCheck = getPotentialMana(); + for(int i = Constants::MTG_NB_COLORS - 1; i > 0 ;i--){ + //sunburst for Ai + if(SunCheck->hasColor(i)){ + if(nextCardToPlay->getManaCost()->hasColor(i) > 0){//do nothing if the card already has this color. + }else{ + if(nextCardToPlay->sunburst < nextCardToPlay->getManaCost()->getConvertedCost()){ + nextCardToPlay->getManaCost()->add(i,1); + nextCardToPlay->getManaCost()->remove(0,1); + nextCardToPlay->sunburst += 1; + } + } + } + } + delete(SunCheck); + } +///////////////////////// + tapLandsForMana(nextCardToPlay->getManaCost()); + } AIAction * a = NEW AIAction(nextCardToPlay); clickstream.push(a); return 1; - }else{ + } + else + { selectAbility(); } + if(p->getManaPool()->getConvertedCost() > 0 && Checked == false)//not the best thing ever, but allows the Ai a chance to double check if its mana pool has something before moving on, atleast one time. + { + Checked = true; + computeActions(); + } break; } case Constants::MTG_PHASE_COMBATATTACKERS: chooseAttackers(); + break; + case Constants::MTG_PHASE_ENDOFTURN: + Checked = false; break; default: selectAbility(); diff --git a/projects/mtg/src/AIStats.cpp b/projects/mtg/src/AIStats.cpp index 4ac8070ca..da6d7d154 100644 --- a/projects/mtg/src/AIStats.cpp +++ b/projects/mtg/src/AIStats.cpp @@ -93,7 +93,7 @@ bool AIStats::isInTop(MTGCardInstance * card, unsigned int max, bool tooSmallCou if (n >= max) return false; AIStat * stat = *it; if (stat->source == id){ - if (stat->value>=0) return true; + if ((stat->value + card->DangerRanking()) >=3) return true; return false; } n++; diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index 12a484271..bbb6be2dc 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -3,162 +3,162 @@ // BanishCard implementations -AABanishCard::AABanishCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost=NULL, int _banishmentType = -1):ActivatedAbility(_id, _source,_cost),banishmentType(_banishmentType) { - if (_target) target = _target; -} - -const char * AABanishCard::getMenuText() -{ - return "Send to graveyard"; -} - -int AABanishCard::resolve() -{ - DebugTrace("This is not implemented!"); - return 0; -} - -AABanishCard * AABanishCard::clone() const{ - AABanishCard * a = NEW AABanishCard(*this); - a->isClone = 1; - return a; -} - -// Bury - -AABuryCard::AABuryCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost = NULL , int _banishmentType = 0):AABanishCard(_id, _source, _target, _cost, AABanishCard::BURY) -{} - -int AABuryCard::resolve(){ - MTGCardInstance * _target = (MTGCardInstance *) target; - if(_target){ - return _target->bury(); - } - return 0; -} - -const char * AABuryCard::getMenuText(){ - return "Bury"; -} - -AABuryCard * AABuryCard::clone() const{ - AABuryCard * a = NEW AABuryCard(*this); - a->isClone = 1; - return a; -} - - -// Destroy - -AADestroyCard::AADestroyCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost = NULL, int _banishmentType = 0):AABanishCard(_id, _source, _target, _cost, AABanishCard::DESTROY) -{} - -int AADestroyCard::resolve(){ - MTGCardInstance * _target = (MTGCardInstance *) target; - if(_target){ - return _target->destroy(); - } - return 0; -} - -const char * AADestroyCard::getMenuText(){ - return "Destroy"; -} - -AADestroyCard * AADestroyCard::clone() const{ - AADestroyCard * a = NEW AADestroyCard(*this); - a->isClone = 1; - return a; -} - -// Sacrifice -AASacrificeCard::AASacrificeCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost = NULL, int _banishmentType = 0):AABanishCard(_id, _source, _target, _cost, AABanishCard::SACRIFICE) { -} - -int AASacrificeCard::resolve(){ - MTGCardInstance * _target = (MTGCardInstance *) target; - if(_target){ +AABanishCard::AABanishCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost=NULL, int _banishmentType = -1):ActivatedAbility(_id, _source,_cost),banishmentType(_banishmentType) { + if (_target) target = _target; +} + +const char * AABanishCard::getMenuText() +{ + return "Send to graveyard"; +} + +int AABanishCard::resolve() +{ + DebugTrace("This is not implemented!"); + return 0; +} + +AABanishCard * AABanishCard::clone() const{ + AABanishCard * a = NEW AABanishCard(*this); + a->isClone = 1; + return a; +} + +// Bury + +AABuryCard::AABuryCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost = NULL , int _banishmentType = 0):AABanishCard(_id, _source, _target, _cost, AABanishCard::BURY) +{} + +int AABuryCard::resolve(){ + MTGCardInstance * _target = (MTGCardInstance *) target; + if(_target){ + return _target->bury(); + } + return 0; +} + +const char * AABuryCard::getMenuText(){ + return "Bury"; +} + +AABuryCard * AABuryCard::clone() const{ + AABuryCard * a = NEW AABuryCard(*this); + a->isClone = 1; + return a; +} + + +// Destroy + +AADestroyCard::AADestroyCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost = NULL, int _banishmentType = 0):AABanishCard(_id, _source, _target, _cost, AABanishCard::DESTROY) +{} + +int AADestroyCard::resolve(){ + MTGCardInstance * _target = (MTGCardInstance *) target; + if(_target){ + return _target->destroy(); + } + return 0; +} + +const char * AADestroyCard::getMenuText(){ + return "Destroy"; +} + +AADestroyCard * AADestroyCard::clone() const{ + AADestroyCard * a = NEW AADestroyCard(*this); + a->isClone = 1; + return a; +} + +// Sacrifice +AASacrificeCard::AASacrificeCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost = NULL, int _banishmentType = 0):AABanishCard(_id, _source, _target, _cost, AABanishCard::SACRIFICE) { +} + +int AASacrificeCard::resolve(){ + MTGCardInstance * _target = (MTGCardInstance *) target; + if(_target){ Player * p = _target->controller(); - WEvent * e = NEW WEventCardSacrifice(_target); - GameObserver * game = GameObserver::GetInstance(); - game->receiveEvent(e); - p->game->putInGraveyard(_target); - return 1; - } - return 0; -} - -const char * AASacrificeCard::getMenuText(){ - return "Sacrifice"; -} - -AASacrificeCard * AASacrificeCard::clone() const{ - AASacrificeCard * a = NEW AASacrificeCard(*this); - a->isClone = 1; - return a; -} - -// Discard - -AADiscardCard::AADiscardCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost = NULL, int _banishmentType = 0):AABanishCard(_id, _source, _target, _cost, AABanishCard::DISCARD) { -} - -int AADiscardCard::resolve(){ - MTGCardInstance * _target = (MTGCardInstance *) target; - if(_target){ + WEvent * e = NEW WEventCardSacrifice(_target); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); + p->game->putInGraveyard(_target); + return 1; + } + return 0; +} + +const char * AASacrificeCard::getMenuText(){ + return "Sacrifice"; +} + +AASacrificeCard * AASacrificeCard::clone() const{ + AASacrificeCard * a = NEW AASacrificeCard(*this); + a->isClone = 1; + return a; +} + +// Discard + +AADiscardCard::AADiscardCard(int _id, MTGCardInstance * _source, MTGCardInstance * _target, ManaCost * _cost = NULL, int _banishmentType = 0):AABanishCard(_id, _source, _target, _cost, AABanishCard::DISCARD) { +} + +int AADiscardCard::resolve(){ + MTGCardInstance * _target = (MTGCardInstance *) target; + if(_target){ Player * p = _target->controller(); - WEvent * e = NEW WEventCardDiscard(_target); - GameObserver * game = GameObserver::GetInstance(); - game->receiveEvent(e); - p->game->putInGraveyard(_target); - return 1; - } - return 0; -} - -const char * AADiscardCard::getMenuText(){ - return "Discard"; -} - -AADiscardCard * AADiscardCard::clone() const{ - AADiscardCard * a = NEW AADiscardCard(*this); - a->isClone = 1; - return a; -} - - -//Mana Redux -AManaRedux::AManaRedux(int id, MTGCardInstance * source, MTGCardInstance * target,int amount,int type):MTGAbility(id,source,target),amount(amount),type(type) { - MTGCardInstance * _target = (MTGCardInstance *)target; -} - -int AManaRedux::addToGame(){ - MTGCardInstance * _target = (MTGCardInstance *)target; - if(amount < 0){ - amount = abs(amount); - if(_target->getManaCost()->hasColor(type)){ - if(_target->getManaCost()->getConvertedCost() >= 1){ - _target->getManaCost()->remove(type,amount); - if(_target->getManaCost()->alternative > 0){ - _target->getManaCost()->alternative->remove(type,amount);} - if(_target->getManaCost()->BuyBack > 0){ - _target->getManaCost()->BuyBack->remove(type,amount);} - } - } - }else{ - _target->getManaCost()->add(type,amount); - if(_target->getManaCost()->alternative > 0){ - _target->getManaCost()->alternative->add(type,amount);} - if(_target->getManaCost()->BuyBack > 0){ - _target->getManaCost()->BuyBack->add(type,amount);} - } - return MTGAbility::addToGame(); -} - -AManaRedux * AManaRedux::clone() const { - AManaRedux * a = NEW AManaRedux(*this); - a->isClone = 1; - return a; -} - -AManaRedux::~AManaRedux(){} + WEvent * e = NEW WEventCardDiscard(_target); + GameObserver * game = GameObserver::GetInstance(); + game->receiveEvent(e); + p->game->putInGraveyard(_target); + return 1; + } + return 0; +} + +const char * AADiscardCard::getMenuText(){ + return "Discard"; +} + +AADiscardCard * AADiscardCard::clone() const{ + AADiscardCard * a = NEW AADiscardCard(*this); + a->isClone = 1; + return a; +} + + +//Mana Redux +AManaRedux::AManaRedux(int id, MTGCardInstance * source, MTGCardInstance * target,int amount,int type):MTGAbility(id,source,target),amount(amount),type(type) { + MTGCardInstance * _target = (MTGCardInstance *)target; +} + +int AManaRedux::addToGame(){ + MTGCardInstance * _target = (MTGCardInstance *)target; + if(amount < 0){ + amount = abs(amount); + if(_target->getManaCost()->hasColor(type)){ + if(_target->getManaCost()->getConvertedCost() >= 1){ + _target->getManaCost()->remove(type,amount); + if(_target->getManaCost()->alternative > 0){ + _target->getManaCost()->alternative->remove(type,amount);} + if(_target->getManaCost()->BuyBack > 0){ + _target->getManaCost()->BuyBack->remove(type,amount);} + } + } + }else{ + _target->getManaCost()->add(type,amount); + if(_target->getManaCost()->alternative > 0){ + _target->getManaCost()->alternative->add(type,amount);} + if(_target->getManaCost()->BuyBack > 0){ + _target->getManaCost()->BuyBack->add(type,amount);} + } + return MTGAbility::addToGame(); +} + +AManaRedux * AManaRedux::clone() const { + AManaRedux * a = NEW AManaRedux(*this); + a->isClone = 1; + return a; +} + +AManaRedux::~AManaRedux(){} diff --git a/projects/mtg/src/DeckManager.cpp b/projects/mtg/src/DeckManager.cpp index 229748891..f90cbee18 100644 --- a/projects/mtg/src/DeckManager.cpp +++ b/projects/mtg/src/DeckManager.cpp @@ -1,6 +1,7 @@ #include "PrecompiledHeader.h" #include "DeckManager.h" +#include "Player.h" #include void DeckManager::updateMetaDataList( vector * refList, bool isAI ) @@ -49,3 +50,15 @@ DeckManager* DeckManager::GetInstance() return mInstance; } + + +// p1 is assumed to be the player you want stats for +// p2 is the opponent +int DeckManager::getDifficultyRating( Player *statsPlayer, Player *player ) +{ + DeckMetaDataList * metas = DeckMetaDataList::decksMetaData; + + DeckMetaData *meta = metas->get( player->deckFile, statsPlayer ); + + return meta->getDifficulty(); +} diff --git a/projects/mtg/src/DeckMetaData.cpp b/projects/mtg/src/DeckMetaData.cpp index 5d61ce22e..cb59ffb3b 100644 --- a/projects/mtg/src/DeckMetaData.cpp +++ b/projects/mtg/src/DeckMetaData.cpp @@ -42,7 +42,7 @@ void DeckMetaData::loadStatsForPlayer( Player * statsPlayer, string deckStatsFil { _difficulty = HARD; } - else if (_percentVictories < 67) + else if (_percentVictories < 55) { _difficulty = NORMAL; } diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 53514e7eb..6ca50971b 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -603,9 +603,9 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG //rather dirty way to stop thises and lords from conflicting with each other. - string prelords[] = {"foreach(","lord(","aslongas(", "all("}; + string prelords[] = {"foreach(","lord(","aslongas(","teach(", "all("}; size_t lord = string::npos; - for (int j = 0; j < 4; ++j){ + for (int j = 0; j < 5; ++j){ size_t found2 = s.find(prelords[j]); if (found2!=string::npos && ((found == string::npos) || found2 < found)){ lord = found2; @@ -674,10 +674,10 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG //Lord, foreach, aslongas - string lords[] = {"lord(","foreach(","aslongas(", "all("}; + string lords[] = {"lord(","foreach(","aslongas(","teach(", "all("}; found = string::npos; i = -1; - for (int j = 0; j < 4; ++j){ + for (int j = 0; j < 5; ++j){ size_t found2 = s.find(lords[j]); if (found2!=string::npos && ((found == string::npos) || found2 < found)){ found = found2; @@ -721,7 +721,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG if (found !=string::npos) oneShot = 1; if (activated) oneShot = 1; if (card->hasType("sorcery") || card->hasType("instant")) oneShot = 1; - if (i == 3) oneShot = 1; + if (i == 4) oneShot = 1; if (a->oneShot) oneShot = 1; Damageable * _target = NULL; if (spell) _target = spell->getNextDamageableTarget(); @@ -740,7 +740,8 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG case 0: result = NEW ALord(id, card, lordTargets, lordIncludeSelf, a); break; case 1: result = NEW AForeach(id, card, _target,lordTargets, lordIncludeSelf, a,mini,maxi); break; case 2: result = NEW AAsLongAs(id, card, _target,lordTargets, lordIncludeSelf, a,mini,maxi); break; - case 3: result = NEW ALord(id, card, lordTargets, lordIncludeSelf, a); break; + case 3: result = NEW ATeach(id, card, lordTargets,lordIncludeSelf, a); break; + case 4: result = NEW ALord(id, card, lordTargets, lordIncludeSelf, a); break; default: result = NULL; } if (result) result->oneShot = oneShot; diff --git a/projects/mtg/src/MTGGameZones.cpp b/projects/mtg/src/MTGGameZones.cpp index 6c369aab1..008ee4c97 100644 --- a/projects/mtg/src/MTGGameZones.cpp +++ b/projects/mtg/src/MTGGameZones.cpp @@ -77,6 +77,84 @@ void MTGPlayerCards::initGame(int shuffle, int draw){ } } +void MTGPlayerCards::OptimizedHand(int amount,int lands,int creatures,int othercards){ +//give the Ai hand adventage to insure a challanging match. + GameObserver * game = game->GetInstance(); + game->currentPlayerId = game->currentPlayerId; + game->currentPlayer = game->currentPlayer; + + if (!game->players[0]->isAI() && game->players[1]->isAI()){ + Player * p = game->players[1]; + MTGCardInstance * card = NULL; + MTGGameZone * z = p->game->library; + MTGGameZone * e = p->game->temp; + int optimizedland = 0; + int optimizedothercards = 0; + int optimizedcreatures = 0; + for (int j = 0; jnb_cards; j++){ + MTGCardInstance * _card = z->cards[j]; +//------------- + if (_card->hasType("Land") && optimizedland < lands){ + card = _card; + if (card){ + p->game->putInZone(card, p->game->library, p->game->hand); + optimizedland += 1; + } + } +//----------------first try to optimize a few cards that cost 2 or less. + if (_card->getManaCost()->getConvertedCost() <= 2 && optimizedothercards < othercards && !_card->hasType("Land") && !_card->hasType("Creature")){ + card = _card; + if (card){ + p->game->putInZone(card, p->game->library, p->game->hand); + optimizedothercards += 1; + } + } + if(_card->getManaCost()->getConvertedCost() <= 2 && optimizedcreatures < creatures && _card->hasType("Creature")){ + card = _card; + if (card){ + p->game->putInZone(card, p->game->library, p->game->hand); + optimizedcreatures += 1; + } + } + } +//--------------incase none of them cost 2 or less(which makes for a really poorly crafted Ai deck), try for 3 or less at this point we're accepting anything but lands under 3 mana--- + for (int k = 0; k < z->nb_cards; k++){ + MTGCardInstance * _card = z->cards[k]; + + if (_card->getManaCost()->getConvertedCost() <= 3 && optimizedothercards < othercards && (!_card->hasType("Land") || _card->hasType("Creature"))) + { + card = _card; + if (card) + { + p->game->putInZone(card, p->game->library, p->game->hand); + optimizedothercards += 1; + } + } + if(_card->getManaCost()->getConvertedCost() <= 3 && optimizedcreatures < creatures && (_card->hasType("Creature") || !_card->hasType("Land"))) + { + card = _card; + if (card) + { + p->game->putInZone(card, p->game->library, p->game->hand); + optimizedcreatures += 1; + } + } + } +//--------------add up remaining. only 7 cards are optimized, the remaining cards (if rules change amount) are just drawn. + int leftover = 0; + leftover = amount; + leftover -= optimizedland; + leftover -= optimizedcreatures; + leftover -= optimizedothercards; + for(int i = leftover; i > 0;i--) + { + p->game->drawFromLibrary(); + } + + } +//---------------------------- +} + void MTGPlayerCards::drawFromLibrary(){ if (!library->nb_cards) { int cantlosers = 0; diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index 9acfef150..54fee1df5 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -1454,23 +1454,23 @@ int MTGAffinityRule::receiveEvent(WEvent * event){ ok = 0; if (e->to == p->game->battlefield) ok = 2;//card enters play if(ok == 2){//enters play from anywhere - if (e->from == p->game->graveyard || e->from == p->game->hand || e->from == p->game->library || e->from == p->game->exile || e->from == p->game->stack || e->from == p->opponent()->game->battlefield || e->from == p->game->temp){ + if (e->from == p->game->graveyard || e->from == p->game->hand || e->from == p->game->library || e->from == p->game->exile || e->from == p->game->stack || e->from == p->game->temp){ //--redux effect MTGGameZone * z = card->controller()->game->hand; int nbcards = z->nb_cards; int colored = 0; string etype = ""; MTGCardInstance * card = e->card->previous; - if (card && card->hasSubtype("artifact")){etype = "art";} - if (card && card->hasSubtype("swamp")){etype = "swa";} - if (card && card->hasSubtype("mountain")){etype = "mou";} - if (card && card->hasSubtype("plains")){etype = "pla";} - if (card && card->hasSubtype("island")){etype = "isl";} - if (card && card->hasSubtype("forest")){etype = "for";} - if (card && card->hasSubtype("creature") && card->hasColor(1)){etype = "cre"; colored = 1;} + if (card && card->hasSubtype("artifact")){etype.append("art");} + if (card && card->hasSubtype("swamp")){etype.append("swa");} + if (card && card->hasSubtype("mountain")){etype.append("mou");} + if (card && card->hasSubtype("plains")){etype.append("pla");} + if (card && card->hasSubtype("island")){etype.append("isl");} + if (card && card->hasSubtype("forest")){etype.append("for");} + if (card && card->hasSubtype("creature") && card->hasColor(1)){etype.append("cre"); colored = 1;} for (int j = 0; j < nbcards; ++j){ MTGCardInstance * c = z->cards[j]; - if ((c->has(Constants::AFFINITYARTIFACTS) && etype == "art") || ( c->has(Constants::AFFINITYSWAMP) && etype == "swa") || ( c->has(Constants::AFFINITYMOUNTAIN) && etype == "mou") ||( c->has(Constants::AFFINITYPLAINS) && etype == "pla") || ( c->has(Constants::AFFINITYISLAND) && etype == "isl") || ( c->has(Constants::AFFINITYFOREST) && etype == "for") || ( c->has(Constants::AFFINITYGREENCREATURES) && etype == "cre")){ + if ((c->has(Constants::AFFINITYARTIFACTS) && etype.find("art")) || ( c->has(Constants::AFFINITYSWAMP) && etype.find("swa")) || ( c->has(Constants::AFFINITYMOUNTAIN) && etype.find("mou")) ||( c->has(Constants::AFFINITYPLAINS) && etype.find("pla")) || ( c->has(Constants::AFFINITYISLAND) && etype.find("isl")) || ( c->has(Constants::AFFINITYFOREST) && etype.find("for")) || ( c->has(Constants::AFFINITYGREENCREATURES) && etype.find("cre"))){ if(c->getManaCost()->getConvertedCost() > 0){ c->getManaCost()->remove(colored,1);//one less colorless to cast }else{ @@ -1490,16 +1490,16 @@ int MTGAffinityRule::receiveEvent(WEvent * event){ int nbcards = z->nb_cards; string etype = ""; MTGCardInstance * card = e->card->previous; - if (card && card->hasSubtype("artifact")){etype = "art";} - if (card && card->hasSubtype("swamp")){etype = "swa";} - if (card && card->hasSubtype("mountain")){etype = "mou";} - if (card && card->hasSubtype("plains")){etype = "pla";} - if (card && card->hasSubtype("island")){etype = "isl";} - if (card && card->hasSubtype("forest")){etype = "for";} - if (card && card->hasSubtype("creature") && card->hasColor(1)){etype = "cre"; colored = 1;} + if (card && card->hasSubtype("artifact")){etype.append("art");} + if (card && card->hasSubtype("swamp")){etype.append("swa");} + if (card && card->hasSubtype("mountain")){etype.append("mou");} + if (card && card->hasSubtype("plains")){etype.append("pla");} + if (card && card->hasSubtype("island")){etype.append("isl");} + if (card && card->hasSubtype("forest")){etype.append("for");} + if (card && card->hasSubtype("creature") && card->hasColor(1)){etype.append("cre"); colored = 1;} for (int j = 0; j < nbcards; ++j){ MTGCardInstance * c = z->cards[j]; - if (c && ((c->has(Constants::AFFINITYARTIFACTS) && etype == "art") || ( c->has(Constants::AFFINITYSWAMP) && etype == "swa") || ( c->has(Constants::AFFINITYMOUNTAIN) && etype == "mou") ||( c->has(Constants::AFFINITYPLAINS) && etype == "pla") || ( c->has(Constants::AFFINITYISLAND) && etype == "isl") || ( c->has(Constants::AFFINITYFOREST) && etype == "for") || ( c->has(Constants::AFFINITYGREENCREATURES) && etype == "cre"))){ + if (c && ((c->has(Constants::AFFINITYARTIFACTS) && etype.find("art")) || ( c->has(Constants::AFFINITYSWAMP) && etype.find("swa")) || ( c->has(Constants::AFFINITYMOUNTAIN) && etype.find("mou")) ||( c->has(Constants::AFFINITYPLAINS) && etype.find("pla")) || ( c->has(Constants::AFFINITYISLAND) && etype.find("isl")) || ( c->has(Constants::AFFINITYFOREST) && etype.find("for")) || ( c->has(Constants::AFFINITYGREENCREATURES) && etype.find("cre")))){ if(c->reduxamount > 0){ c->reduxamount -= 1; }else{ diff --git a/projects/mtg/src/Rules.cpp b/projects/mtg/src/Rules.cpp index 341382515..3b67f7a86 100644 --- a/projects/mtg/src/Rules.cpp +++ b/projects/mtg/src/Rules.cpp @@ -8,6 +8,8 @@ #include "MTGGameZones.h" #include "MTGAbility.h" +#include "DeckManager.h" +#include "AIPlayer.h" int Rules::getMTGId(string cardName){ int cardnb = atoi(cardName.c_str()); @@ -133,17 +135,39 @@ void Rules::addExtraRules(){ MTGCardInstance::ExtraRules[i].lastController = p; for (size_t j = 0; j< initState.playerData[i].extraRules.size(); ++j){ AbilityFactory af; + MTGPlayerCards * hand = NULL; + int handsize = 7; + int difficultyRating = 0; MTGAbility * a = af.parseMagicLine(initState.playerData[i].extraRules[j], id++, NULL,&MTGCardInstance::ExtraRules[i]); - if (a){ + if(p->playMode != Player::MODE_TEST_SUITE && g->mRules->gamemode != GAME_TYPE_MOMIR && g->mRules->gamemode != GAME_TYPE_RANDOM1 && g->mRules->gamemode != GAME_TYPE_RANDOM2 && g->mRules->gamemode != GAME_TYPE_STORY)//keep this out of mimor and other game modes. + { + difficultyRating = DeckManager::getDifficultyRating( g->players[0], g->players[1] ); + } + + if (a){ if (a->oneShot){ + if(p->isAI() && a->aType == MTGAbility::STANDARD_DRAW && difficultyRating == EASY && p->playMode != Player::MODE_TEST_SUITE && g->mRules->gamemode != GAME_TYPE_MOMIR && g->mRules->gamemode != GAME_TYPE_RANDOM1 && g->mRules->gamemode != GAME_TYPE_RANDOM2 && g->mRules->gamemode != GAME_TYPE_STORY)//stupid protections to keep this out of mimor and other game modes. + { + handsize = a->nbcardAmount; + ((AIPlayer *)p)->forceBestAbilityUse = true; + ((AIPlayer *)p)->agressivity += 100; + hand->OptimizedHand(handsize,3,1,3);//easy decks get a major boost, open hand is 2lands,1 creature under 3 mana,3spells under 3 mana. + } + else if(p->isAI() && a->aType == MTGAbility::STANDARD_DRAW && difficultyRating == NORMAL && p->playMode != Player::MODE_TEST_SUITE && g->mRules->gamemode != GAME_TYPE_MOMIR && g->mRules->gamemode != GAME_TYPE_RANDOM1 && g->mRules->gamemode != GAME_TYPE_RANDOM2 && g->mRules->gamemode != GAME_TYPE_STORY)//stupid protections to keep this out of mimor and other game modes. + { + handsize = a->nbcardAmount; + hand->OptimizedHand(handsize,1,0,2);//give the Ai deck a tiny boost by giving it 1 land and 2 spells under 3 manacost. + }else{//resolve normally if the deck is listed as hard. a->resolve(); + } delete(a); }else{ a->addToGame(); } + } - } - } + } + } for (size_t j = 0; j< extraRules.size(); ++j){ AbilityFactory af; diff --git a/projects/mtg/src/StoryFlow.cpp b/projects/mtg/src/StoryFlow.cpp index 585bfddb0..c17379699 100644 --- a/projects/mtg/src/StoryFlow.cpp +++ b/projects/mtg/src/StoryFlow.cpp @@ -289,8 +289,8 @@ void StoryDuel::init(){ GameObserver::Init(players, 2); game = GameObserver::GetInstance(); + rules->gamemode = GAME_TYPE_STORY; game->startGame(rules); - } StoryDuel::StoryDuel(TiXmlElement* root,StoryFlow * mParent): StoryPage(mParent) { game = NULL;