string AIHints::constraintsNotFulfilled(AIAction * action, AIHint * hint, ManaCost * potentialMana) { std::stringstream out; if (!action) { if (hint->mCombatAttackTip.size()) { out << "to see if this can attack[" << hint->mCombatAttackTip << "]"; return out.str(); } 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->getCost())) { 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->getCost()); for (int i = 0; i < Constants::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 ""; }
int AIMomirPlayer::getEfficiency(OrderedAIAction * action) { MTGAbility * ability = action->ability; ManaCost * cost = ability->getCost(); if (cost && !(cost->isExtraPaymentSet())) return 0; //Does not handle abilities with sacrifice yet int efficiency = AIPlayerBaka::getEfficiency(action); if (observer->getCurrentGamePhase() < MTG_PHASE_FIRSTMAIN) return 0; return efficiency; }
//Finds a mtgAbility matching the Hint description, and returns a valid AIAction matching this mtgability RankingContainer AIHints::findActions(AIHint * hint) { RankingContainer ranking; vector<MTGAbility *> 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->getCost())) { mPlayer->createAbilityTargets(a, card, ranking); //TODO make that function static? break; //For performance... ? } } } return ranking; }
int AIMomirPlayer::momir() { if (!game->hand->nb_cards) return 0; //nothing to discard :/ int result = 0; int opponentCreatures = getCreaturesInfo(opponent(), INFO_NBCREATURES); int myCreatures = getCreaturesInfo(this, INFO_NBCREATURES ); ManaCost * potentialMana = getPotentialMana(); int converted = potentialMana->getConvertedCost(); SAFE_DELETE(potentialMana); int efficiency = 100; int chance = 1 + (randomGenerator.random() % 100); if (converted == 5 && myCreatures > opponentCreatures && game->hand->nb_cards < 4) efficiency = 5; //Strategy: skip 5 drop if (converted == 7 && myCreatures > opponentCreatures && game->hand->nb_cards < 2) efficiency = 50; //Strategy: 7 drops have bad upkeep costs and the AI doesn't handle those right now... if (converted > 8) converted = 8; if (converted == 8) efficiency = 100 - (myCreatures - opponentCreatures); if (efficiency >= chance) { std::vector<int16_t> _cost; _cost.push_back(Constants::MTG_COLOR_ARTIFACT); _cost.push_back(converted); ManaCost * cost = NEW ManaCost(_cost); MTGAbility * ability = getMomirAbility(); MTGCardInstance * card = game->hand->cards[0]; if (ability->isReactingToClick(card, cost)) { payTheManaCost(cost); AIAction * a = NEW AIAction(this, ability, card); clickstream.push(a); result = 1; } delete cost; } return result; }
int Counters::removeCounter(const char * _name, int _power, int _toughness) { for (int i = 0; i < mCount; i++) { if (counters[i]->sameAs(_name, _power, _toughness)) { if (counters[i]->nb < 1) return 0; counters[i]->removed(); counters[i]->nb--; GameObserver *g = target->getObserver(); WEvent * e = NEW WEventCounters(this,_name,_power,_toughness,false,true); dynamic_cast<WEventCounters*>(e)->targetCard = this->target; g->receiveEvent(e); // special case: when the last time counter is removed from non-suspended card // sacrifice that card if (!target->suspended && counters[i]->name == "time" && counters[i]->nb == 0) { MTGCardInstance * beforeCard = target; target->controller()->game->putInGraveyard(target); WEvent * e = NEW WEventCardSacrifice(beforeCard, target); g->receiveEvent(e); } //special case:if a card is suspended and no longer has a time counter when the last is removed, the card is cast. if (target->suspended && !target->counters->hasCounter("time",0,0)) { GameObserver * game = target->getObserver(); MTGAbility *ac = NEW AACastCard(game, game->mLayers->actionLayer()->getMaxId(), target, target, false, false, true, "", "", false, false); MayAbility *ma1 = NEW MayAbility(game, game->mLayers->actionLayer()->getMaxId(), ac->clone(), target, true); MTGAbility *ga1 = NEW GenericAddToGame(game, game->mLayers->actionLayer()->getMaxId(), target, NULL, ma1->clone()); SAFE_DELETE(ac); SAFE_DELETE(ma1); ga1->resolve(); SAFE_DELETE(ga1); } return mCount; } } return 0; }
WEvent * REDrawReplacement::replace(WEvent *event) { if (!event) return event; WEventDraw * e = dynamic_cast<WEventDraw*> (event); if (!e) return event; if (DrawerOfCard != e->player) return event; if(!replacementAbility) return event; //check for dredge TargetChooserFactory tf(e->player->getObserver()); TargetChooser * tcb = NULL; tcb = tf.createTargetChooser("dredgeable",source->source); tcb->targetter = NULL; if(tcb->validTargetsExist()) { if(e->player == DrawerOfCard && e->player == source->source->controller()) { SAFE_DELETE(tcb); return event; } } SAFE_DELETE(tcb); vector<MTGAbility*>selection; //look for other draw replacement effects list<ReplacementEffect *>::iterator it; GameObserver * game = source->source->getObserver(); if(replacementAbility->source->controller() == DrawerOfCard) for (it = game->replacementEffects->modifiers.begin(); it != game->replacementEffects->modifiers.end(); it++) { if(REDrawReplacement * DR = dynamic_cast<REDrawReplacement *>(*it)) { MTGAbility * otherA = NULL; if(DR->DrawerOfCard == e->player) { if(DR->replacementAbility->oneShot) selection.push_back(DR->replacementAbility->clone()); else { otherA = NEW GenericAddToGame(game, game->mLayers->actionLayer()->getMaxId(), source->source,NULL,DR->replacementAbility->clone()); selection.push_back(otherA); } } } } for(int j = 0; j < e->nb_cards; j++) { if(e->player != DrawerOfCard || selection.size() < 2) { MTGAbility * toResolve = replacementAbility->clone(); if(replacementAbility->oneShot) toResolve->resolve(); else toResolve->addToGame(); } else { MTGAbility * menuChoice = NEW MenuAbility(game, 1, source->source, source->source,true,selection,NULL,"Choose Draw Replacement"); menuChoice->addToGame(); } } delete event; event = NULL; return event; }
void Rules::addExtraRules(GameObserver* g) { int id = g->mLayers->actionLayer()->getMaxId(); MTGAllCards::sortSubtypeList(); for (int i = 0; i < 2; ++i) { Player * p = g->players[i]; //Trick so that the abilities don't die; g->ExtraRules[i].currentZone = p->game->inPlay; g->ExtraRules[i].lastController = p; g->ExtraRules[i].owner = p; for (size_t j = 0; j < initState.playerData[i].extraRules.size(); ++j) { AbilityFactory af(g); MTGPlayerCards * hand = NULL; int handsize = 7; int difficultyRating = 0; int Optimizedhandcheat = options[Options::OPTIMIZE_HAND].number; MTGAbility * a = af.parseMagicLine(initState.playerData[i].extraRules[j], id++, NULL, &(g->ExtraRules[i])); 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 && g->mRules->gamemode != GAME_TYPE_DEMO && (!g->players[0] == PLAYER_TYPE_CPU && !g->players[1] == PLAYER_TYPE_CPU) #ifdef NETWORK_SUPPORT && !g->players[1] == PLAYER_TYPE_REMOTE #endif //NETWORK_SUPPORT )//keep this out of momir and other game modes. { difficultyRating = g->getDeckManager()->getDifficultyRating(g->players[0], g->players[1]); } if (a) { //We make those non interruptible, so that they don't appear on the player's stack a->canBeInterrupted = false; if (a->oneShot) { if (((p->isAI() && p->playMode != Player::MODE_AI && p->opponent()->playMode != Player::MODE_AI)||( !p->isAI() && Optimizedhandcheat)) && a->aType == MTGAbility::STANDARD_DRAW && difficultyRating != HARD && 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 = ((AADrawer *)a)->getNumCards(); if(difficultyRating == EASY) { ((AIPlayer *) p)->forceBestAbilityUse = true; ((AIPlayer *) p)->agressivity += 100; hand->OptimizedHand(p,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 (difficultyRating == NORMAL) { hand->OptimizedHand(p,handsize, 1, 0, 2);//give the Ai deck a tiny boost by giving it 1 land and 2 spells under 3 manacost. } else { hand->OptimizedHand(p,handsize, 3, 1, 3);//no rating fall out case. } } 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(g); MTGAbility * a = af.parseMagicLine(extraRules[j], id++, NULL, &(g->ExtraRules[0])); if (a) { if (a->oneShot) { a->resolve(); delete (a); } else { a->addToGame(); } } } }