From 691a1e1b9168e40191bfb1f122b3fdf8e85d5225 Mon Sep 17 00:00:00 2001 From: "wagic.the.homebrew" Date: Thu, 5 May 2011 14:27:46 +0000 Subject: [PATCH] - Fixed a Bug where AI would not block any attacker in Demo mode (bug introduced in r2759) - Fixed a Bug where AI would not correctly assign blockers if the first attacker is super strong. - Added a hack to prevent AI from an infinite loop while choosing a target. There are edge cases where the AI gets to choose the targets for a TargetChooser that doesn't belong to it. I couldn't dig too long for the root cause, so I added a "return 0" when the case happens. Should probably open a ticket - Added a "Hint" System in AI decks, to help the AI with its strategy. This is not really usable yet, it only works with abilities (not cards to play), and I only added some basic code for counters and tokens. This can probably be extended, but let's wait until we see it working on that other game I'm working on, before rushing into adding hints to all AI decks... - minor cleanup of AI Code --- projects/mtg/Android/jni/Android.mk | 1 + projects/mtg/Makefile | 2 +- projects/mtg/include/AIHints.h | 43 +++++ projects/mtg/include/AIPlayer.h | 5 + projects/mtg/include/MTGDeck.h | 2 + projects/mtg/include/Player.h | 1 + projects/mtg/src/AIHints.cpp | 254 ++++++++++++++++++++++++++ projects/mtg/src/AIPlayer.cpp | 110 ++++++++--- projects/mtg/src/CardGui.cpp | 2 +- projects/mtg/src/MTGDeck.cpp | 12 +- projects/mtg/src/Player.cpp | 16 +- projects/mtg/src/Rules.cpp | 5 +- projects/mtg/src/WResourceManager.cpp | 2 +- projects/mtg/template.vcxproj | 2 + projects/mtg/template.vcxproj.filters | 6 + 15 files changed, 423 insertions(+), 40 deletions(-) create mode 100644 projects/mtg/include/AIHints.h create mode 100644 projects/mtg/src/AIHints.cpp diff --git a/projects/mtg/Android/jni/Android.mk b/projects/mtg/Android/jni/Android.mk index 996b008bd..d160eb345 100644 --- a/projects/mtg/Android/jni/Android.mk +++ b/projects/mtg/Android/jni/Android.mk @@ -30,6 +30,7 @@ LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.cpp \ $(MTG_PATH)/src/ActionElement.cpp \ $(MTG_PATH)/src/ActionLayer.cpp \ $(MTG_PATH)/src/ActionStack.cpp \ + $(MTG_PATH)/src/AIHints.cpp \ $(MTG_PATH)/src/AIMomirPlayer.cpp \ $(MTG_PATH)/src/AIPlayer.cpp \ $(MTG_PATH)/src/AIStats.cpp \ diff --git a/projects/mtg/Makefile b/projects/mtg/Makefile index d027502c6..b2ca186fc 100644 --- a/projects/mtg/Makefile +++ b/projects/mtg/Makefile @@ -1,4 +1,4 @@ -OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/AllAbilities.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/CardSelectorSingleton.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckEditorMenu.o objs/DeckMenu.o objs/DeckMenuItem.o objs/DeckMetaData.o objs/DeckStats.o objs/DuelLayers.o objs/Effects.o objs/ExtraCost.o objs/GameApp.o objs/GameLauncher.o objs/GameObserver.o objs/GameOptions.o objs/GameState.o objs/GameStateAwards.o objs/GameStateDeckViewer.o objs/GameStateDuel.o objs/DeckManager.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GameStateStory.o objs/GameStateTransitions.o objs/GuiAvatars.o objs/GuiBackground.o objs/GuiCardsController.o objs/GuiCombat.o objs/GuiFrame.o objs/GuiHand.o objs/GuiLayers.o objs/GuiMana.o objs/GuiPhaseBar.o objs/GuiPlay.o objs/GuiStatic.o objs/ManaCost.o objs/ManaCostHybrid.o objs/MenuItem.o objs/ModRules.o objs/MTGAbility.o objs/MTGCardInstance.o objs/MTGCard.o objs/MTGDeck.o objs/MTGDefinitions.o objs/MTGGamePhase.o objs/MTGGameZones.o objs/MTGPack.o objs/MTGRules.o objs/Navigator.o objs/ObjectAnalytics.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/PlayRestrictions.o objs/Pos.o objs/PrecompiledHeader.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/SimplePopup.o objs/StoryFlow.o objs/StyleManager.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/ThisDescriptor.o objs/Token.o objs/Translate.o objs/TranslateKeys.o objs/Trash.o objs/utils.o objs/WEvent.o objs/WResourceManager.o objs/WCachedResource.o objs/WDataSrc.o objs/WGui.o objs/WFilter.o objs/Tasks.o objs/WFont.o +OBJS = objs/ActionElement.o objs/ActionLayer.o objs/ActionStack.o objs/AIHints.o objs/AIMomirPlayer.o objs/AIPlayer.o objs/AIStats.o objs/AllAbilities.o objs/CardGui.o objs/CardDescriptor.o objs/CardDisplay.o objs/CardEffect.o objs/CardPrimitive.o objs/CardSelector.o objs/CardSelectorSingleton.o objs/Counters.o objs/Credits.o objs/Damage.o objs/DamagerDamaged.o objs/DeckDataWrapper.o objs/DeckEditorMenu.o objs/DeckMenu.o objs/DeckMenuItem.o objs/DeckMetaData.o objs/DeckStats.o objs/DuelLayers.o objs/Effects.o objs/ExtraCost.o objs/GameApp.o objs/GameLauncher.o objs/GameObserver.o objs/GameOptions.o objs/GameState.o objs/GameStateAwards.o objs/GameStateDeckViewer.o objs/GameStateDuel.o objs/DeckManager.o objs/GameStateMenu.o objs/GameStateOptions.o objs/GameStateShop.o objs/GameStateStory.o objs/GameStateTransitions.o objs/GuiAvatars.o objs/GuiBackground.o objs/GuiCardsController.o objs/GuiCombat.o objs/GuiFrame.o objs/GuiHand.o objs/GuiLayers.o objs/GuiMana.o objs/GuiPhaseBar.o objs/GuiPlay.o objs/GuiStatic.o objs/ManaCost.o objs/ManaCostHybrid.o objs/MenuItem.o objs/ModRules.o objs/MTGAbility.o objs/MTGCardInstance.o objs/MTGCard.o objs/MTGDeck.o objs/MTGDefinitions.o objs/MTGGamePhase.o objs/MTGGameZones.o objs/MTGPack.o objs/MTGRules.o objs/Navigator.o objs/ObjectAnalytics.o objs/OptionItem.o objs/PhaseRing.o objs/Player.o objs/PlayerData.o objs/PlayGuiObjectController.o objs/PlayGuiObject.o objs/PlayRestrictions.o objs/Pos.o objs/PrecompiledHeader.o objs/PriceList.o objs/ReplacementEffects.o objs/Rules.o objs/SimpleMenu.o objs/SimpleMenuItem.o objs/SimplePad.o objs/SimplePopup.o objs/StoryFlow.o objs/StyleManager.o objs/Subtypes.o objs/TargetChooser.o objs/TargetsList.o objs/TextScroller.o objs/ThisDescriptor.o objs/Token.o objs/Translate.o objs/TranslateKeys.o objs/Trash.o objs/utils.o objs/WEvent.o objs/WResourceManager.o objs/WCachedResource.o objs/WDataSrc.o objs/WGui.o objs/WFilter.o objs/Tasks.o objs/WFont.o DEPS = $(patsubst objs/%.o, deps/%.d, $(OBJS)) RESULT = $(shell psp-config --psp-prefix 2> Makefile.cache) diff --git a/projects/mtg/include/AIHints.h b/projects/mtg/include/AIHints.h new file mode 100644 index 000000000..6d3a764f6 --- /dev/null +++ b/projects/mtg/include/AIHints.h @@ -0,0 +1,43 @@ +#ifndef _AIHINTS_H_ +#define _AIHINTS_H_ + +#include +#include +using std::string; +using std::vector; + +#include "AIPlayer.h" + +class ManaCost; +class MTGAbility; + +class AIHint +{ +public: + string mCondition; + string mAction; + int mSourceId; + AIHint(string line); +}; + + +class AIHints +{ +protected: + AIPlayer * mPlayer; + vector hints; + AIHint * getByCondition (string condition); + AIAction * findAbilityRecursive(AIHint * hint, ManaCost * potentialMana); + vector findAbilities(AIHint * hint); + RankingContainer findActions(AIHint * hint); + string constraintsNotFulfilled(AIAction * a, AIHint * hint, ManaCost * potentialMana); + bool findSource(int sourceId); + bool abilityMatches(MTGAbility * a, AIHint * hint); +public: + AIHints (AIPlayer * player); + AIAction * suggestAbility(ManaCost * potentialMana); + void add(string line); + ~AIHints(); +}; + +#endif \ No newline at end of file diff --git a/projects/mtg/include/AIPlayer.h b/projects/mtg/include/AIPlayer.h index 487781e42..885ebf83a 100644 --- a/projects/mtg/include/AIPlayer.h +++ b/projects/mtg/include/AIPlayer.h @@ -20,6 +20,7 @@ using std::queue; class AIStats; +class AIHints; class AIAction { @@ -72,6 +73,7 @@ class AIPlayer: public Player{ protected: //Variables used by Test suite MTGCardInstance * nextCardToPlay; + AIHints * hints; queue clickstream; bool tapLandsForMana(ManaCost * cost, MTGCardInstance * card = NULL); int orderBlockers(); @@ -87,6 +89,9 @@ protected: // returns 1 if the AI algorithm supports a given cost (ex:simple mana cost), 0 otherwise (ex: cost involves Sacrificing a target) int CanHandleCost(ManaCost * cost); + //Tries to play an ability recommended by the deck creator + int selectHintAbility(); + public: AIStats * stats; int agressivity; diff --git a/projects/mtg/include/MTGDeck.h b/projects/mtg/include/MTGDeck.h index 5b06687cd..98592542e 100644 --- a/projects/mtg/include/MTGDeck.h +++ b/projects/mtg/include/MTGDeck.h @@ -156,6 +156,8 @@ public: map cards; string meta_desc; string meta_name; + vector meta_AIHints; + int meta_id; int totalCards(); int totalPrice(); diff --git a/projects/mtg/include/Player.h b/projects/mtg/include/Player.h index 0ec320a65..4fa974570 100644 --- a/projects/mtg/include/Player.h +++ b/projects/mtg/include/Player.h @@ -30,6 +30,7 @@ public: bool nomaxhandsize; bool isPoisoned; MTGPlayerCards * game; + MTGDeck * mDeck; string deckFile; string deckFileSmall; string deckName; diff --git a/projects/mtg/src/AIHints.cpp b/projects/mtg/src/AIHints.cpp new file mode 100644 index 000000000..5ca268d81 --- /dev/null +++ b/projects/mtg/src/AIHints.cpp @@ -0,0 +1,254 @@ +#include "PrecompiledHeader.h" + +#include "AIHints.h" +#include "AIPlayer.h" +#include "utils.h" +#include "AllAbilities.h" + +#include + +AIHint::AIHint(string _line) +{ + string line = _line; + if (!line.length()) + { + DebugTrace("AIHINTS: line is empty"); + return; + } + std::transform(line.begin(), line.end(), line.begin(), ::tolower); + vector parameters = split(line,':'); + mCondition = (parameters.size() == 1)? "" : parameters[0]; + string action = parameters[parameters.size() - 1]; + + vector splitAction = parseBetween(action, "sourceid(", ")"); + if (splitAction.size()) + { + mAction = splitAction[0]; + mSourceId = atoi(splitAction[1].c_str()); + } + else + { + mAction = action; + mSourceId = 0; + } +} + +AIHints::AIHints(AIPlayer * player): mPlayer(player) +{ +} + +void AIHints::add(string line) +{ + hints.push_back(NEW AIHint(line)); +} + +AIHints::~AIHints() +{ + for (size_t i = 0; i < hints.size(); ++i) + SAFE_DELETE(hints[i]); + hints.clear(); +} + +AIHint * AIHints::getByCondition (string condition) +{ + if (!condition.size()) + return NULL; + + for (size_t i = 0; i < hints.size(); ++i) + { + if (hints[i]->mCondition.compare(condition) == 0) + return hints[i]; + } + return NULL; +} + +//return true if a given ability matches a hint's description +//Eventually this will look awfully similar to the parser...any way to merge them somehow ? +bool AIHints::abilityMatches(MTGAbility * ability, AIHint * hint) +{ + string s = hint->mAction; + + MTGAbility * a = AbilityFactory::getCoreAbility(ability); + + //Here we want to check that the card reacting to the MTGAbility is the one mentioned in the hint, + // to avoid mistaking the MTGAbility with a similar one. + //Ideally we would find all cards with this ID, and see if the ability reacts to a click on one of these cards. + // This is the poor man's version, based on the fact that most cards are the source of their own abilities + if (hint->mSourceId && ((!a->source) || a->source->getMTGId() != hint->mSourceId)) + return false; + + if ( AACounter * counterAbility = dynamic_cast (a) ) + { + vector splitCounter = parseBetween(s, "counter(", ")"); + if (!splitCounter.size()) + return false; + + string counterstring = counterAbility->name; + std::transform(counterstring.begin(), counterstring.end(), counterstring.begin(), ::tolower); + return (splitCounter[1].compare(counterstring) == 0); + } + + if ( ATokenCreator * tokenAbility = dynamic_cast (a) ) + { + vector splitToken = parseBetween(s, "token(", ")"); + if (!splitToken.size()) + return false; + return (tokenAbility->tokenId && tokenAbility->tokenId == atoi(splitToken[1].c_str())); + } + + return false; +} + +//Finds all mtgAbility matching the Hint description +// For now we limit findings +vector AIHints::findAbilities(AIHint * hint) +{ + std::vector elems; + ActionLayer * al = GameObserver::GetInstance()->mLayers->actionLayer(); + + for (int i = 1; i < al->mCount; i++) //0 is not a mtgability...hackish + { + MTGAbility * a = ((MTGAbility *) al->mObjects[i]); + if (abilityMatches(a, hint)) + elems.push_back(a); + } + return elems; + +} + +//Finds a mtgAbility matching the Hint description, and returns a valid AIAction matching this mtgability +RankingContainer AIHints::findActions(AIHint * hint) +{ + RankingContainer ranking; + + vector abilities = findAbilities(hint); + + for (size_t i = 0; i < abilities.size(); ++i) + { + MTGAbility * a = abilities[i]; + + for (int j = 0; j < mPlayer->game->inPlay->nb_cards; j++) + { + MTGCardInstance * card = mPlayer->game->inPlay->cards[j]; + if (a->isReactingToClick(card, a->cost)) + { + mPlayer->createAbilityTargets(a, card, ranking); //TODO make that function static? + break; //For performance... ? + } + } + } + + return ranking; +} + +//Returns true if a card with the given MTG ID exists +bool AIHints::findSource(int sourceId) +{ + for (int i = 0; i < mPlayer->game->inPlay->nb_cards; i++) + { + MTGCardInstance * c = mPlayer->game->inPlay->cards[i]; + if (c->getMTGId() == sourceId) + return true; + } + return false; +} + +string AIHints::constraintsNotFulfilled(AIAction * action, AIHint * hint, ManaCost * potentialMana) +{ + std::stringstream out; + + if (!action) + { + if (hint->mSourceId && !findSource(hint->mSourceId)) + { + out << "needcardinplay[" << hint->mSourceId << "]"; + return out.str(); + } + out << "needability[" << hint->mAction << "]"; + return out.str(); + } + + MTGAbility * a = action->ability; + if (!a) + return "not supported"; + + MTGCardInstance * card = action->click; + if (!card) + return "not supported"; + + //dummy test: would the ability work if we were sure to fulfill its mana requirements? + if (!a->isReactingToClick(card, a->cost)) + { + DebugTrace("This shouldn't happen, this AIAction doesn't seem like a good choice"); + return "not supported"; + } + + if (!a->isReactingToClick(card, potentialMana)) + { + //Not enough Mana, try to find which mana we should get in priority + ManaCost * diff = potentialMana->Diff(a->cost); + for (int i = 0; i < Constants::MTG_NB_COLORS; i++) + { + if(diff->getCost(i) < 0) + { + out << "needmana[" << Constants::MTGColorChars[i] << "]"; + if (Constants::MTGColorChars[i] == 'r') + DebugTrace("Got it"); + SAFE_DELETE(diff); + return out.str(); + } + + } + + //TODO, handle more cases where the cost cannot be paid + return "not supported, can't afford cost for some reason"; + } + + //No problem found, we believe this is a good action to perform + return ""; + +} + +AIAction * AIHints::findAbilityRecursive(AIHint * hint, ManaCost * potentialMana) +{ + RankingContainer ranking = findActions(hint); + + AIAction * a = NULL; + if (ranking.size()) + { + a = NEW AIAction(ranking.begin()->first); + } + + string s = constraintsNotFulfilled(a, hint, potentialMana); + if (s.size()) + { + SAFE_DELETE(a); + AIHint * nextHint = getByCondition(s); + DebugTrace("**I Need " << s << ", this can be provided by " << (nextHint ? nextHint->mAction : "NULL") << "\n\n"); + if (nextHint && nextHint != hint) + return findAbilityRecursive(nextHint, potentialMana); + return NULL; + } + + return a; + +} + +AIAction * AIHints::suggestAbility(ManaCost * potentialMana) +{ + for (size_t i = 0; i < hints.size(); ++i) + { + //Don't suggest abilities that require a condition, for now + if (hints[i]->mCondition.size()) + continue; + + AIAction * a = findAbilityRecursive(hints[i], potentialMana); + if (a) + { + DebugTrace("**I Decided that the best to fulfill " << hints[i]->mAction << " is to play " << a->ability->getMenuText() << "\n\n"); + return a; + } + + } + return NULL; +} \ No newline at end of file diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index 4fe4a5de5..e0d41af7a 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -9,6 +9,7 @@ #include "GuiCombat.h" #include "GameStateDuel.h" #include "DeckManager.h" +#include "AIHints.h" const char * const MTG_LAND_TEXTS[] = { "artifact", "forest", "island", "mountain", "swamp", "plains", "other lands" }; @@ -27,7 +28,7 @@ AIAction::AIAction(MTGCardInstance * c, MTGCardInstance * t) // if we're not in text mode, always get the thumb if (CardSelectorSingleton::Instance()->GetDrawMode() != DrawMode::kText) { - DebugTrace("Prefetching AI card going into play: " << c->getImageName()); + //DebugTrace("Prefetching AI card going into play: " << c->getImageName()); WResourceManager::Instance()->RetrieveCard(c, RETRIEVE_THUMB); // also cache the large image if we're using kNormal mode @@ -71,6 +72,15 @@ AIPlayer::AIPlayer(string file, string fileSmall, MTGDeck * deck) : agressivity = 50; forceBestAbilityUse = false; playMode = Player::MODE_AI; + + //Initialize "AIHints" system + hints = NULL; + if (mDeck && mDeck->meta_AIHints.size()) + { + hints = NEW AIHints(this); + for (size_t i = 0; i < mDeck->meta_AIHints.size(); ++i) + hints->add(mDeck->meta_AIHints[i]); + } } AIPlayer::~AIPlayer() @@ -86,6 +96,7 @@ AIPlayer::~AIPlayer() SAFE_DELETE(action); clickstream.pop(); } + SAFE_DELETE(hints); } MTGCardInstance * AIPlayer::chooseCard(TargetChooser * tc, MTGCardInstance * source, int random) { @@ -125,15 +136,12 @@ bool AIPlayer::tapLandsForMana(ManaCost * cost, MTGCardInstance * target) DebugTrace("Mana cost is NULL. "); return false; } - ManaCost * pMana = NULL; - if(!target) - pMana = getPotentialMana(); - else - pMana = getPotentialMana(target); + ManaCost * pMana = target ? getPotentialMana(target) : getPotentialMana(); + if(!pMana->canAfford(cost)) { - delete pMana; - return false; + delete pMana; + return false; } ManaCost * diff = pMana->Diff(cost); delete (pMana); @@ -670,7 +678,6 @@ int AIAction::getEfficiency() AADrawer * drawer = (AADrawer *)a; //adding this case since i played a few games where Ai litterally decided to mill himself to death. fastest and easiest win ever. //this should help a little, tho ultimately it will be decided later what the best course of action is. - efficiency = 0; //eff of drawing ability is calculated by base 20 + the amount of cards in library minus the amount of cards in hand times 7. //drawing is never going to return a hundred eff because later eff is multiplied by 1.3 if no cards in hand. efficiency = int(20 + p->game->library->nb_cards) - int(p->game->hand->nb_cards * 7); @@ -769,6 +776,32 @@ int AIPlayer::createAbilityTargets(MTGAbility * a, MTGCardInstance * c, RankingC return 1; } +int AIPlayer::selectHintAbility() +{ + if (!hints) + return 0; + + ManaCost * totalPotentialMana = getPotentialMana(); + + AIAction * action = hints->suggestAbility(totalPotentialMana); + if (action && ((WRand() % 100) < 95)) //95% chance + { + if (!clickstream.size()) + { + DebugTrace("AIPlayer:Using Activated ability"); + if (tapLandsForMana(action->ability->cost, action->click)) + { + clickstream.push(action); + SAFE_DELETE(totalPotentialMana); + return 1; + } + } + } + SAFE_DELETE(action); + SAFE_DELETE(totalPotentialMana); + return 0; +} + int AIPlayer::selectAbility() { static bool findingAbility = false; @@ -783,6 +816,15 @@ int AIPlayer::selectAbility() return 0; } findingAbility = true;//im looking now safely! + + + // Try Deck hints first + if (selectHintAbility()) + { + findingAbility = false;//ok to start looking again. + return 1; + } + RankingContainer ranking; list::iterator it; GameObserver * g = GameObserver::GetInstance(); @@ -826,6 +868,7 @@ int AIPlayer::selectAbility() } } } + findingAbility = false;//ok to start looking again. return 1; } @@ -864,16 +907,24 @@ int AIPlayer::chooseTarget(TargetChooser * _tc, Player * forceTarget,MTGCardInst int checkOnly = 0; if (tc) { - if(!Choosencard) - checkOnly = 1; + if(!Choosencard) + checkOnly = 1; } else { - tc = gameObs->getCurrentTargetChooser(); - + tc = gameObs->getCurrentTargetChooser(); } if (!tc) return 0; + + if (tc->source->controller() != this) + { + DebugTrace("AIPLAYER: Error, was asked to chose targets but I don't own the source of the targetController\n"); + return 0; + } + //Make sure we own the decision to choose the targets + assert(tc->source->controller() == this); + tc->initTargets(); //cleanup the targetchooser just in case. if (!(gameObs->currentlyActing() == this)) return 0; @@ -1091,14 +1142,27 @@ int AIPlayer::chooseBlockers() //kick the ai out of this function. map opponentsToughness; int opponentForce = getCreaturesInfo(opponent(), INFO_CREATURESPOWER); + + //Initialize the list of opponent's attacking cards toughness + CardDescriptor cdAttackers; + cdAttackers.init(); + cdAttackers.setType("Creature"); + MTGCardInstance * card = NULL; + while ((card = cdAttackers.nextmatch(opponent()->game->inPlay, card))) + { + if (card->isAttacker()) + opponentsToughness[card] = card->toughness; + } + + //A Descriptor to find untapped creatures in our game CardDescriptor cd; cd.init(); cd.setType("Creature"); cd.unsecureSetTapped(-1); - MTGCardInstance * card = NULL; + card = NULL; MTGAbility * a = g->mLayers->actionLayer()->getAbility(MTGAbility::MTG_BLOCK_RULE); - + // We first try to block the major threats, those that are marked in the Top 3 of our stats while ((card = cd.nextmatch(game->inPlay, card))) { g->mLayers->actionLayer()->reactToClick(a, card); @@ -1130,6 +1194,9 @@ int AIPlayer::chooseBlockers() } } } + + //If blocking one of the major threats is not enough to kill it, + // We change strategy, first we unassign its blockers that where assigned above card = NULL; while ((card = cd.nextmatch(game->inPlay, card))) { @@ -1141,6 +1208,8 @@ int AIPlayer::chooseBlockers() } } } + + //Assign the "free" potential blockers to attacking creatures that are not blocked enough card = NULL; while ((card = cd.nextmatch(game->inPlay, card))) { @@ -1158,8 +1227,7 @@ int AIPlayer::chooseBlockers() { MTGCardInstance * attacker = card->defenser; if (opponentsToughness[attacker] <= 0 || (card->toughness <= attacker->power && opponentForce * 2 < life - && !canFirstStrikeKill(card, attacker)) || attacker->nbOpponents() > 1 - || attacker->controller()->isAI()) + && !canFirstStrikeKill(card, attacker)) || attacker->nbOpponents() > 1) { g->mLayers->actionLayer()->reactToClick(a, card); } @@ -1200,9 +1268,7 @@ int AIPlayer::affectCombatDamages(CombatStep step) //TODO: Deprecate combatDamages int AIPlayer::combatDamages() { - //int result = 0; - GameObserver * gameObs = GameObserver::GetInstance(); - int currentGamePhase = gameObs->getCurrentGamePhase(); + int currentGamePhase = GameObserver::GetInstance()->getCurrentGamePhase(); if (currentGamePhase == Constants::MTG_PHASE_COMBATBLOCKERS) return orderBlockers(); @@ -1609,12 +1675,14 @@ int AIPlayerBaka::Act(float dt) return 0; } interruptIfICan(); + + //computeActions only when i have priority if (!(g->currentlyActing() == this)) { DebugTrace("Cannot interrupt"); return 0; } - if (clickstream.empty() && g->currentlyActing() == this)//computeActions only when i have priority + if (clickstream.empty()) computeActions(); if (clickstream.empty()) { diff --git a/projects/mtg/src/CardGui.cpp b/projects/mtg/src/CardGui.cpp index 2484c6563..7b3f89a1c 100644 --- a/projects/mtg/src/CardGui.cpp +++ b/projects/mtg/src/CardGui.cpp @@ -796,7 +796,7 @@ void CardGui::RenderBig(MTGCard* card, const Pos& pos) return; } - DebugTrace("Unable to fetch image: " << card->getImageName()); + //DebugTrace("Unable to fetch image: " << card->getImageName()); // If we come here, we do not have the picture. AlternateRender(card, pos); diff --git a/projects/mtg/src/MTGDeck.cpp b/projects/mtg/src/MTGDeck.cpp index 395edb676..e1cc6e21a 100644 --- a/projects/mtg/src/MTGDeck.cpp +++ b/projects/mtg/src/MTGDeck.cpp @@ -720,6 +720,12 @@ MTGDeck::MTGDeck(const char * config_file, MTGAllCards * _allcards, int meta_onl meta_desc.append(s.substr(found + 5)); continue; } + found = s.find("HINT:"); + if (found != string::npos) + { + meta_AIHints.push_back(s.substr(found + 5)); + continue; + } continue; } if (meta_only) break; @@ -874,7 +880,8 @@ int MTGDeck::add(MTGDeck * deck) int MTGDeck::add(int cardid) { - if (!database->getCardById(cardid)) return 0; + if (!database->getCardById(cardid)) + return 0; if (cards.find(cardid) == cards.end()) { cards[cardid] = 1; @@ -890,7 +897,8 @@ int MTGDeck::add(int cardid) int MTGDeck::add(MTGCard * card) { - if (!card) return 0; + if (!card) + return 0; return (add(card->getId())); } diff --git a/projects/mtg/src/Player.cpp b/projects/mtg/src/Player.cpp index c9334e2ea..63607df47 100644 --- a/projects/mtg/src/Player.cpp +++ b/projects/mtg/src/Player.cpp @@ -8,12 +8,8 @@ Player::Player(string file, string fileSmall, MTGDeck * deck) : Damageable(20) { - bool deleteDeckPlease = false; if(deck == NULL && file != "testsuite" && file != "remote") - { deck = NEW MTGDeck(file.c_str(), MTGCollection()); - deleteDeckPlease = true; - } game = NULL; deckFile = file; @@ -28,14 +24,11 @@ Damageable(20) playMode = MODE_HUMAN; if (deck != NULL) { - game = NEW MTGPlayerCards(deck); - game->setOwner(this); - deckName = deck->meta_name; - } - if(deleteDeckPlease) - { - SAFE_DELETE(deck); + game = NEW MTGPlayerCards(deck); + game->setOwner(this); + deckName = deck->meta_name; } + mDeck = deck; } /*Method to call at the end of a game, before all objects involved in the game are destroyed */ @@ -50,6 +43,7 @@ Player::~Player() SAFE_DELETE(game); WResourceManager::Instance()->Release(mAvatarTex); mAvatarTex = NULL; + SAFE_DELETE(mDeck); } void Player::loadAvatar(string file) diff --git a/projects/mtg/src/Rules.cpp b/projects/mtg/src/Rules.cpp index 1ce83e774..9a57e4dea 100644 --- a/projects/mtg/src/Rules.cpp +++ b/projects/mtg/src/Rules.cpp @@ -336,7 +336,6 @@ Player * Rules::loadPlayerMomir(int isAI) else player = NEW AIMomirPlayer(options.profileFile("momir.txt", "", true).c_str(), deckFileSmall, empty, tempDeck); - delete tempDeck; return player; } @@ -371,7 +370,6 @@ Player * Rules::loadPlayerRandom(int isAI, int mode) else player = NEW AIPlayerBaka(deckFile, deckFileSmall, "", tempDeck); - delete tempDeck; return player; } @@ -518,7 +516,8 @@ void Rules::postUpdateInit() if (postUpdateInitDone) return; for (int i = 0; i < 2; ++ i) - GameObserver::GetInstance()->players[i]->getManaPool()->copy(initState.playerData[i].manapool); + GameObserver::GetInstance()->players[i]->getManaPool()->add(initState.playerData[i].manapool); + // GameObserver::GetInstance()->players[i]->getManaPool()->copy(initState.playerData[i].manapool); postUpdateInitDone = true; } diff --git a/projects/mtg/src/WResourceManager.cpp b/projects/mtg/src/WResourceManager.cpp index 697013b92..83bd055d2 100644 --- a/projects/mtg/src/WResourceManager.cpp +++ b/projects/mtg/src/WResourceManager.cpp @@ -1178,7 +1178,7 @@ cacheItem* WCache::Get(int id, const string& filename, i if (!it->second) { mError = CACHE_ERROR_404; - DebugTrace("cache hit, no item??"); + //DebugTrace("cache hit, no item??"); //assert(false); } return it->second; //A hit, or maybe a miss. diff --git a/projects/mtg/template.vcxproj b/projects/mtg/template.vcxproj index ab4af142b..519f5bf71 100644 --- a/projects/mtg/template.vcxproj +++ b/projects/mtg/template.vcxproj @@ -301,6 +301,7 @@ + @@ -431,6 +432,7 @@ + diff --git a/projects/mtg/template.vcxproj.filters b/projects/mtg/template.vcxproj.filters index 25457ba1e..af1c60c7c 100644 --- a/projects/mtg/template.vcxproj.filters +++ b/projects/mtg/template.vcxproj.filters @@ -307,6 +307,9 @@ src + + src + @@ -636,6 +639,9 @@ inc + + inc +