//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<AACounter *> (a) ) { vector<string> 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<ATokenCreator *> (a) ) { vector<string> splitToken = parseBetween(s, "token(", ")"); if (!splitToken.size()) return false; return (tokenAbility->tokenId && tokenAbility->tokenId == atoi(splitToken[1].c_str())); } return false; }
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); mCondition = line; string action = line; vector<string> splitAction = parseBetween(action, "sourceid(", ")"); if (splitAction.size()) { mAction = splitAction[0]; mSourceId = atoi(splitAction[1].c_str()); } else { mAction = action; mSourceId = 0; } vector<string> splitDontAttack = parseBetween(action, "dontattackwith(", ")"); if(splitDontAttack.size()) { mCombatAttackTip = splitDontAttack[1]; } vector<string> splitCastOrder = parseBetween(action, "castpriority(", ")"); if(splitCastOrder.size()) { castOrder = split(splitCastOrder[1],','); } if(action.find( "combo ") != string::npos) { string Combo = ""; Combo = action.c_str() + 6; combos.push_back(Combo); } }
ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstance * c) { ManaCost * manaCost; GameObserver* g = c?c->getObserver():NULL; if (_manaCost) { manaCost = _manaCost; } else { manaCost = NEW ManaCost(); } manaCost->xColor = -1; int state = 0; size_t start = 0; size_t end = 0; while (!s.empty() && state != -1) { switch (state) { case 0: start = s.find_first_of("{"); if(s.find_first_of("{") != string::npos && start > 0) { string value = s.substr(start -1,end); if(value == "n{")//"restrictio n{m orbid} would read the n{m as {m} millcost return manaCost; } if (start == string::npos) { return manaCost; } else { state = 1; } break; case 1: end = s.find_first_of("}"); if (end == string::npos) { state = -1; } else { string value = s.substr(start + 1, end - 1 - start); if (value == "u") { manaCost->add(Constants::MTG_COLOR_BLUE, 1); } else if (value == "b") { manaCost->add(Constants::MTG_COLOR_BLACK, 1); } else if (value == "w") { manaCost->add(Constants::MTG_COLOR_WHITE, 1); } else if (value == "g") { manaCost->add(Constants::MTG_COLOR_GREEN, 1); } else if (value == "r") { manaCost->add(Constants::MTG_COLOR_RED, 1); } else { //Parse target for extraCosts TargetChooserFactory tcf(g); TargetChooser * tc = NULL; size_t target_start = value.find("("); size_t target_end = value.find(")"); if (target_start != string::npos && target_end != string::npos) { string target = value.substr(target_start + 1, target_end - 1 - target_start); tc = tcf.createTargetChooser(target, c); } //switch on the first letter. If two costs share their first letter, add an "if" within the switch std::transform(value.begin(), value.end(), value.begin(), ::tolower); switch (value[0]) { case 'x': if(value == "x") { manaCost->x(); } else { vector<string>colorSplit = parseBetween(value,"x:"," ",false); if(colorSplit.size()) { int color = -1; const string ColorStrings[] = { Constants::kManaColorless, Constants::kManaGreen, Constants::kManaBlue, Constants::kManaRed, Constants::kManaBlack, Constants::kManaWhite }; for (unsigned int i = 0; i < sizeof(ColorStrings)/sizeof(ColorStrings[0]); ++i) { if (s.find(ColorStrings[i]) != string::npos) { color = i; } } manaCost->specificX(color); } } break; case 'v': if (value.find("value:") != string::npos) { vector<string> splitParsedVar = parseBetween(value, "value:", " ", false); WParsedInt* res = NEW WParsedInt(splitParsedVar[1], NULL, c); manaCost->add(Constants::MTG_COLOR_ARTIFACT, res->getValue()); SAFE_DELETE(res); } break; case 't': //Tap if (value == "t") { manaCost->addExtraCost(NEW TapCost); } else { manaCost->addExtraCost(NEW TapTargetCost(tc)); } break; case 's': if (value.find("s2l") != string::npos) { //Send To Library Cost (move from anywhere to Library) manaCost->addExtraCost(NEW ToLibraryCost(tc)); } else if (value.find("s2g") != string::npos) { //Send to Graveyard Cost (move from anywhere to Graveyard) manaCost->addExtraCost(NEW ToGraveCost(tc)); } else { //Sacrifice manaCost->addExtraCost(NEW SacrificeCost(tc)); } break; case 'e': //Exile manaCost->addExtraCost(NEW ExileTargetCost(tc)); break; case 'h': //bounce (move to Hand) manaCost->addExtraCost(NEW BounceTargetCost(tc)); break; case 'l': if (value == "l2e") { //Mill to exile yourself as a cost (Library 2 Exile) manaCost->addExtraCost(NEW MillExileCost(tc)); } else if (value == "l") { //Life cost manaCost->addExtraCost(NEW LifeCost(tc)); } else { //Specific Life cost vector<string>valSplit = parseBetween(value,"l:"," ",false); if (valSplit.size()) { WParsedInt* lifetopay = NEW WParsedInt(valSplit[1], NULL, c); manaCost->addExtraCost(NEW SpecificLifeCost(tc,lifetopay->getValue())); SAFE_DELETE(lifetopay); } } break; case 'd': //DiscardRandom cost if (value.find("delve") != string::npos) { if(!tc) tc = tcf.createTargetChooser("*|mygraveyard", c); manaCost->addExtraCost(NEW Delve(tc)); } else if (value == "d") { manaCost->addExtraCost(NEW DiscardRandomCost(tc)); } else { manaCost->addExtraCost(NEW DiscardCost(tc)); } break; case 'm': //Mill yourself as a cost manaCost->addExtraCost(NEW MillCost(tc)); break; case 'n': //return unblocked attacker cost { TargetChooserFactory tcf(g); tc = tcf.createTargetChooser("creature|myBattlefield", c); manaCost->addExtraCost(NEW Ninja(tc)); break; } case 'k': //kill offering { TargetChooserFactory tcf(g); if (value == "kgoblin") { tc = tcf.createTargetChooser("creature[goblin]|myBattlefield", c); } else if (value == "kfox") { tc = tcf.createTargetChooser("creature[fox]|myBattlefield", c); } else if (value == "kmoonfolk") { tc = tcf.createTargetChooser("creature[moonfolk]|myBattlefield", c); } else if (value == "krat") { tc = tcf.createTargetChooser("creature[rat]|myBattlefield", c); } else if (value == "ksnake") { tc = tcf.createTargetChooser("creature[snake]|myBattlefield", c); } //TODO iterate subtypes of creatures manaCost->addExtraCost(NEW Offering(tc)); break; } case 'p' : { SAFE_DELETE(tc); size_t start = value.find("("); size_t end = value.rfind(")"); string manaType = value.substr(start + 1, end - start - 1); manaCost->addExtraCost(NEW LifeorManaCost(NULL,manaType)); break; } case 'i' : { SAFE_DELETE(tc); manaCost->add(0,1); manaCost->addExtraCost(NEW SnowCost); break; } case 'q': if(value == "q") { manaCost->addExtraCost(NEW UnTapCost); } else { manaCost->addExtraCost(NEW UnTapTargetCost(tc)); } break; case 'c': //Counters or cycle { if (value.find("convoke") != string::npos) { if (!tc) tc = tcf.createTargetChooser("creature|mybattlefield", c); manaCost->addExtraCost(NEW Convoke(tc)); } else if(value == "chosencolor") { if(c) manaCost->add(c->chooseacolor, 1); } else if(value == "cycle") { manaCost->addExtraCost(NEW CycleCost(tc)); } else if(value.find("(") != string::npos) { size_t counter_start = value.find("("); size_t counter_end = value.find(")", counter_start); AbilityFactory abf(g); string counterString = value.substr(counter_start + 1, counter_end - counter_start - 1); Counter * counter = abf.parseCounter(counterString, c); size_t separator = value.find(",", counter_start); size_t separator2 = string::npos; if (separator != string::npos) { separator2 = value.find(",", counter_end + 1); } SAFE_DELETE(tc); size_t target_start = string::npos; if (separator2 != string::npos) { target_start = value.find(",", counter_end + 1); } size_t target_end = value.length(); if (target_start != string::npos && target_end != string::npos) { string target = value.substr(target_start + 1, target_end - 1 - target_start); tc = tcf.createTargetChooser(target, c); } manaCost->addExtraCost(NEW CounterCost(counter, tc)); break; } else if(value == "c") { manaCost->add(Constants::MTG_COLOR_WASTE, 1); break; } break; } default: //uncolored cost and hybrid costs and special cost { if(value == "unattach") { manaCost->addExtraCost(NEW UnattachCost(c)); break; } int intvalue = atoi(value.c_str()); int colors[2]; int values[2]; if (intvalue < 10 && value.size() > 1) { for (int i = 0; i < 2; i++) { char c = value[i]; if (c >= '0' && c <= '9') { colors[i] = Constants::MTG_COLOR_ARTIFACT; values[i] = c - '0'; } else { for (int j = 0; j < Constants::NB_Colors; j++) { if (c == Constants::MTGColorChars[j]) { colors[i] = j; values[i] = 1; } } } } if (values[0] > 0 || values[1] > 0) manaCost->addHybrid(colors[0], values[0], colors[1], values[1]); } else { manaCost->add(Constants::MTG_COLOR_ARTIFACT, intvalue); } break; } } } s = s.substr(end + 1); state = 0; } break; default: break; } } return manaCost; }
//MTGAllCards int MTGAllCards::processConfLine(string &s, MTGCard *card, CardPrimitive * primitive) { if ('#' == s[0]) return 1; // a comment shouldn't be treated as an error condition size_t del_pos = s.find_first_of('='); if (del_pos == string::npos || 0 == del_pos) return 0; s[del_pos] = '\0'; const string key = s.substr(0, del_pos); const string val = s.substr(del_pos + 1); switch (key[0]) { case 'a': if (key == "auto") { if (!primitive) primitive = NEW CardPrimitive(); primitive->addMagicText(val); } else if (StartsWith(key, "auto")) { if (!primitive) primitive = NEW CardPrimitive(); primitive->addMagicText(val, key.substr(4)); } else if (key == "alias") { if (!primitive) primitive = NEW CardPrimitive(); primitive->alias = atoi(val.c_str()); } else if (key == "abilities") { if (!primitive) primitive = NEW CardPrimitive(); string value = val; //Specific Abilities std::transform(value.begin(), value.end(), value.begin(), ::tolower); vector<string> values = split(value, ','); for (size_t values_i = 0; values_i < values.size(); ++values_i) { for (int j = Constants::NB_BASIC_ABILITIES - 1; j >= 0; --j) { if (values[values_i].find(Constants::MTGBasicAbilities[j]) != string::npos) { primitive->basicAbilities[j] = 1; break; } } } } break; case 'b': //buyback if (!primitive) primitive = NEW CardPrimitive(); if (ManaCost * cost = primitive->getManaCost()) { string value = val; std::transform(value.begin(), value.end(), value.begin(), ::tolower); cost->setBuyback(ManaCost::parseManaCost(value)); } break; case 'c': //color if (!primitive) primitive = NEW CardPrimitive(); { string value = val; std::transform(value.begin(), value.end(), value.begin(), ::tolower); vector<string> values = split(value, ','); int removeAllOthers = 1; for (size_t values_i = 0; values_i < values.size(); ++values_i) { primitive->setColor(values[values_i], removeAllOthers); removeAllOthers = 0; } } break; case 'd'://dredge if (!primitive) primitive = NEW CardPrimitive(); { string value = val; std::transform(value.begin(), value.end(), value.begin(), ::tolower); vector<string> values = parseBetween(value,"dredge(",")"); if(values.size()) primitive->dredgeAmount = atoi(values[1].c_str()); break; } case 'f': //flashback//morph { if (!primitive) primitive = NEW CardPrimitive(); if(ManaCost * cost = primitive->getManaCost()) { if( s.find("facedown") != string::npos)//morph { string value = val; std::transform(value.begin(), value.end(), value.begin(), ::tolower); cost->setMorph(ManaCost::parseManaCost(value)); } else { string value = val; std::transform(value.begin(), value.end(), value.begin(), ::tolower); cost->setFlashback(ManaCost::parseManaCost(value)); } } break; } case 'g': //grade if (s.size() - del_pos - 1 > 2) currentGrade = getGrade(val[2]); break; case 'i': //id if (!card) card = NEW MTGCard(); card->setMTGId(atoi(val.c_str())); break; case 'k': //kicker if (!primitive) primitive = NEW CardPrimitive(); if (ManaCost * cost = primitive->getManaCost()) { string value = val; std::transform(value.begin(), value.end(), value.begin(), ::tolower); size_t multikick = value.find("multi"); bool isMultikicker = false; if(multikick != string::npos) { size_t endK = value.find("{",multikick); value.erase(multikick, endK - multikick); isMultikicker = true; } cost->setKicker(ManaCost::parseManaCost(value)); cost->getKicker()->isMulti = isMultikicker; } break; case 'm': //mana if (!primitive) primitive = NEW CardPrimitive(); { string value = val; std::transform(value.begin(), value.end(), value.begin(), ::tolower); primitive->setManaCost(value); } break; case 'n': //name if (!primitive) primitive = NEW CardPrimitive(); primitive->setName(val); break; case 'o': //othercost/otherrestriction if (!primitive) primitive = NEW CardPrimitive(); if(key[5] == 'r')//otherrestrictions { string value = val; primitive->setOtherRestrictions(value); } else { if (ManaCost * cost = primitive->getManaCost()) { string value = val; std::transform(value.begin(), value.end(), value.begin(), ::tolower); size_t name = value.find("name("); string theName = ""; if(name != string::npos) { size_t endName = value.find(")",name); theName = value.substr(name + 5,endName - name - 5); value.erase(name, endName - name + 1); } cost->setAlternative(ManaCost::parseManaCost(value)); if(theName.size()) cost->getAlternative()->alternativeName.append(theName); } } break; case 'p': if (key[1] == 'r') { // primitive if (!card) card = NEW MTGCard(); map<string, CardPrimitive*>::iterator it = primitives.find(val); if (it != primitives.end()) card->setPrimitive(it->second); } else { //power if (!primitive) primitive = NEW CardPrimitive(); primitive->setPower(atoi(val.c_str())); } break; case 'r': //retrace/rarity//restrictions if(key[2] == 's' && key[3] == 't')//restrictions { if (!primitive) primitive = NEW CardPrimitive(); string value = val; primitive->setRestrictions(value); } else if (key[1] == 'e' && key[2] == 't') { //retrace if (!primitive) primitive = NEW CardPrimitive(); if (ManaCost * cost = primitive->getManaCost()) { string value = val; std::transform(value.begin(), value.end(), value.begin(), ::tolower); cost->setRetrace(ManaCost::parseManaCost(value)); } } else if (s.find("rar") != string::npos) { //rarity if (!card) card = NEW MTGCard(); card->setRarity(val[0]); } break; case 's': //subtype, suspend { if (s.find("suspend") != string::npos) { size_t time = s.find("suspend("); size_t end = s.find(")="); int suspendTime = atoi(s.substr(time + 8,end - 2).c_str()); if (!primitive) primitive = NEW CardPrimitive(); if (ManaCost * cost = primitive->getManaCost()) { string value = val; std::transform(value.begin(), value.end(), value.begin(), ::tolower); cost->setSuspend(ManaCost::parseManaCost(value)); primitive->suspendedTime = suspendTime; } } else { if (!primitive) primitive = NEW CardPrimitive(); vector<string> values = split(val.c_str(), ' '); for (size_t values_i = 0; values_i < values.size(); ++values_i) primitive->setSubtype(values[values_i]); } break; } case 't': if (!primitive) primitive = NEW CardPrimitive(); if (key == "target") { string value = val; std::transform(value.begin(), value.end(), value.begin(), ::tolower); primitive->spellTargetType = value; } else if (key == "text") primitive->setText(val); else if (key == "type") { vector<string> values = split(val, ' '); for (size_t values_i = 0; values_i < values.size(); ++values_i) primitive->setType(values[values_i]); } else if (key == "toughness") primitive->setToughness(atoi(val.c_str())); break; default: if(primitive) { DebugTrace( endl << "MTGDECK Parsing Error: " << " [" << primitive->getName() << "]" << s << std::endl); } else { DebugTrace( endl << "MTGDECK Parsing Generic Error: " << s << std::endl); } break; } tempPrimitive = primitive; tempCard = card; return del_pos; }
bool AIHints::HintSaysItsForCombo(GameObserver* observer,MTGCardInstance * card) { TargetChooserFactory tfc(observer); TargetChooser * hintTc = NULL; bool forCombo = false; for(unsigned int i = 0; i < hints.size();i++) { if (hints[i]->combos.size()) { //time to find the parts and condiations of the combo. string part = ""; if(!hints[i]->partOfCombo.size() && hints[i]->combos.size()) { for(unsigned int cPart = 0; cPart < hints[i]->combos.size(); cPart++) { //here we disect the different parts of a given combo part = hints[i]->combos[cPart]; hints[i]->partOfCombo = split(part,'^'); for(unsigned int dPart = 0; dPart < hints[i]->partOfCombo.size(); dPart++) { vector<string>asTc; asTc = parseBetween(hints[i]->partOfCombo[dPart],"hold(",")"); if(asTc.size()) { hints[i]->hold.push_back(asTc[1]); asTc.clear(); } asTc = parseBetween(hints[i]->partOfCombo[dPart],"until(",")"); if(asTc.size()) { hints[i]->until.push_back(asTc[1]); asTc.clear(); } asTc = parseBetween(hints[i]->partOfCombo[dPart],"restriction{","}"); if(asTc.size()) { hints[i]->restrict.push_back(asTc[1]); asTc.clear(); } asTc = parseBetween(hints[i]->partOfCombo[dPart],"cast(",")"); if(asTc.size()) { hints[i]->cardTargets[asTc[1]] = parseBetween(hints[i]->partOfCombo[dPart],"targeting(",")")[1]; } asTc = parseBetween(hints[i]->partOfCombo[dPart],"totalmananeeded(",")"); if(asTc.size()) { hints[i]->manaNeeded = asTc[1]; asTc.clear(); } } } }//we collect the peices of a combo on first run. for(unsigned int hPart = 0; hPart < hints[i]->hold.size(); hPart++) { hintTc = tfc.createTargetChooser(hints[i]->hold[hPart],card); if(hintTc && hintTc->canTarget(card,true) && hintTc->countValidTargets() <= hintTc->maxtargets) { forCombo = true; } SAFE_DELETE(hintTc); } } } return forCombo;//return forCombo that way all hints that are combos are predisected. }