From ed03d2f3ef17dbddf8ad8cb3bcf868cb54b4efe9 Mon Sep 17 00:00:00 2001 From: "omegablast2002@yahoo.com" Date: Sat, 6 Nov 2010 06:59:43 +0000 Subject: [PATCH] added optimizing of opponents hand if the opponent deck is listed as "easy". slight optimize if listed as "normal" none if listed as "hard" this provides slightly more challange from even poorly constructed Ai decks. taught Ai to pump creatures during combat, and more so if its heading directly at player, taught Ai that its better to use "becomes" and "transforms" during first main. this allow its to actually attack with the manlands ect. new ability lord...teach(whatever[whatever]) ability. teach is a targeted lord, it takes the cards current target and lords it the ability. im aware of a tiny memleak it contains, but the leak is happening on parser lvl, so i need more eyes to look at it. teach is ideally used for equipment, and was designed to fix issue 244 taught abilities are not given to the source cards. forced Ai to pay for sunburst correctly. it was choosing to pay with all of one type of mana. now it pays either max or 1 from max sunburst. added a tiny double check for Ai to try and find something to use if it suddenly has mana in its pool. it is only a single check in a turn, but i notice it actually does slightly improve the usages of dark ritual and foreach mana producers. ideally i wanted it to check EVERYTIME. but i could not achieve it without putting the game in danger of looping. so once is better then none :/ fixed a bug with affinity where it was not counting duel lands, this is becuase of not setting it up correctly for lands with multiple types SORRY! --- projects/mtg/include/AIPlayer.h | 7 +- projects/mtg/include/AllAbilities.h | 234 ++++++++++++++------- projects/mtg/include/DeckManager.h | 6 + projects/mtg/include/MTGAbility.h | 4 + projects/mtg/include/MTGGameZones.h | 1 + projects/mtg/src/AIPlayer.cpp | 77 ++++++- projects/mtg/src/AIStats.cpp | 2 +- projects/mtg/src/AllAbilities.cpp | 314 ++++++++++++++-------------- projects/mtg/src/DeckManager.cpp | 13 ++ projects/mtg/src/DeckMetaData.cpp | 2 +- projects/mtg/src/MTGAbility.cpp | 13 +- projects/mtg/src/MTGGameZones.cpp | 78 +++++++ projects/mtg/src/MTGRules.cpp | 34 +-- projects/mtg/src/Rules.cpp | 30 ++- projects/mtg/src/StoryFlow.cpp | 2 +- 15 files changed, 548 insertions(+), 269 deletions(-) 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;