ManaCost::ManaCost(ManaCost * manaCost) { init(); if ( !manaCost ) return; for (int i = 0; i <= Constants::NB_Colors; i++) { cost[i] = manaCost->getCost(i); } hybrids = manaCost->hybrids; kicker = NEW ManaCost(manaCost->kicker); if (kicker) kicker->isMulti = manaCost->isMulti; Retrace = NEW ManaCost( manaCost->Retrace ); BuyBack = NEW ManaCost( manaCost->BuyBack ); alternative = NEW ManaCost( manaCost->alternative ); FlashBack = NEW ManaCost( manaCost->FlashBack ); morph = NEW ManaCost( manaCost->morph ); suspend = NEW ManaCost( manaCost->suspend ); Bestow = NEW ManaCost(manaCost->Bestow); extraCosts = NULL; if (manaCost->extraCosts) { extraCosts = manaCost->extraCosts->clone(); } manaUsedToCast = NULL; xColor = manaCost->xColor; }
ManaCost::ManaCost(const ManaCost& manaCost) #ifdef TRACK_OBJECT_USAGE : InstanceCounter<ManaCost>(manaCost) #endif { for (int i = 0; i <= Constants::NB_Colors; i++) { cost.push_back(manaCost.cost[i]); } hybrids = manaCost.hybrids; // make new copies of the pointers for the deep copy kicker = NEW ManaCost( manaCost.kicker ); Retrace = NEW ManaCost( manaCost.Retrace ); BuyBack = NEW ManaCost( manaCost.BuyBack ); alternative = NEW ManaCost( manaCost.alternative ); FlashBack = NEW ManaCost( manaCost.FlashBack ); morph = NEW ManaCost( manaCost.morph ); suspend = NEW ManaCost( manaCost.suspend ); Bestow = NEW ManaCost(manaCost.Bestow); extraCosts = NULL; if (manaCost.extraCosts) { extraCosts = manaCost.extraCosts->clone(); } manaUsedToCast = NULL; xColor = manaCost.xColor; }
Player::Player(GameObserver *observer, string file, string fileSmall, MTGDeck * deck) : Damageable(observer, 20), mAvatarName(""), offerInterruptOnPhase(MTG_PHASE_DRAW) { if(deck == NULL && file != "testsuite" && file != "remote" && file != "") deck = NEW MTGDeck(file.c_str(), MTGCollection()); premade = false; game = NULL; deckFile = file; deckFileSmall = fileSmall; handsize = 0; manaPool = NEW ManaPool(this); nomaxhandsize = false; poisonCount = 0; damageCount = 0; preventable = 0; mAvatarTex = NULL; type_as_damageable = DAMAGEABLE_PLAYER; playMode = MODE_HUMAN; skippingTurn = 0; extraTurn = 0; drawCounter = 0; epic = 0; forcefield = 0; raidcount = 0; handmodifier = 0; snowManaG = 0; snowManaR = 0; snowManaB = 0; snowManaU = 0; snowManaW = 0; snowManaC = 0; prowledTypes.clear(); doesntEmpty = NEW ManaCost(); poolDoesntEmpty = NEW ManaCost(); if (deck != NULL) { game = NEW MTGPlayerCards(deck); // This automatically sets the observer pointer on all the deck cards game->setOwner(this); deckName = deck->meta_name; } else { game = new MTGPlayerCards(); game->setOwner(this); } mDeck = deck; }
AbBlink::AbBlink(int id): Ability(id) { m_name = "Risky Displacement"; m_description = "Teleports the caster to a random space within sight"; m_cost.push_back(ManaCost(1,COLOR_BLUE)); m_color = COLOR_BLUE; }
AbLightningBolt::AbLightningBolt(int id): Ability(id) { m_name = "Lightning Bolt"; m_description = "Conjures a bolt of lightning that strikes a single target"; m_cost.push_back(ManaCost(1,COLOR_RED)); m_color = COLOR_RED; }
AbDefensiveStance::AbDefensiveStance(int id): Ability(id) { m_name = "Defensive Stance"; m_description = "A stance that reduces the damage taken but hinders your movement"; m_cost.push_back(ManaCost(5,COLOR_WHITE)); m_color = COLOR_WHITE; }
AbInstantTunnel::AbInstantTunnel(int id): Ability(id) { m_name = "Instant Tunnel"; m_description = "Blasts away rock to form a crude tunnel"; m_cost.push_back(ManaCost(2,COLOR_RED)); m_color = COLOR_RED; }
AbSacredNectar::AbSacredNectar(int id): Ability(id) { m_name = "Sacred Nectar"; m_description = "Conjures a holy elixir that replenishes your health"; m_cost.push_back(ManaCost(3,COLOR_WHITE)); m_nectar = 0; m_color = COLOR_WHITE; }
AbBlastTrap::AbBlastTrap(int id, int type): Ability(id) { m_name = "Blast Trap"; m_description = "Conjures a rune that explodes when an enemy steps on it"; m_cost.push_back(ManaCost(2,COLOR_BLUE)); m_trap = 0; m_color = COLOR_BLUE; }
Spell::Spell(GameObserver* observer, MTGCardInstance * _source) : Interruptible(observer, 0) { source = _source; mHeight = 40; type = ACTION_SPELL; cost = NEW ManaCost(); cost->extraCosts = NULL; tc = NULL; from = _source->getCurrentZone(); payResult = ManaCost::MANA_UNPAID; source->castMethod = Constants::NOT_CAST; }
int ManaCost::pay(ManaCost * _cost) { int result = MANA_PAID; ManaCost * toPay = NEW ManaCost(); toPay->copy(_cost); ManaCost * diff = Diff(toPay); for (int i = 0; i < Constants::NB_Colors; i++) { cost[i] = diff->getCost(i); } delete diff; delete toPay; return result; //TODO return 0 if can't afford the cost! }
Spell::Spell(GameObserver* observer, int id, MTGCardInstance * _source, TargetChooser * tc, ManaCost * _cost, int payResult) : Interruptible(observer, id), tc(tc), cost(_cost), payResult(payResult) { if (!cost) { cost = NEW ManaCost(); cost->extraCosts = NULL; } source = _source; mHeight = 40; type = ACTION_SPELL; from = _source->getCurrentZone(); _source->backupTargets.clear(); if (tc) { Targetable* t = NULL; for(size_t i = 0; i < tc->getNbTargets(); i++) { t = tc->getNextTarget(t); _source->backupTargets.push_back(t); } } // fill information on how the card came into this zone. Right now the quickest way is to do it here, based on how the mana was paid... switch(payResult) { case ManaCost::MANA_UNPAID: source->castMethod = Constants::NOT_CAST; break; case ManaCost::MANA_PAID: case ManaCost::MANA_PAID_WITH_KICKER: source->castMethod = Constants::CAST_NORMALLY; break; default: source->castMethod = Constants::CAST_ALTERNATE; break; } }
int Spell::resolve() { MTGCardInstance * oldStored = source->storedCard; Player * playerT = source->playerTarget; if (!source->hasType(Subtypes::TYPE_INSTANT) && !source->hasType(Subtypes::TYPE_SORCERY) && source->name.size()) { Player * p = source->controller(); int castMethod = source->castMethod; vector<Targetable*>backupTgt = source->backupTargets; if(from != source->currentZone) { from = source->currentZone;//this happens when casting spells that belong to another player or casting a copy of someone elses spell. } source = p->game->putInZone(source, from, p->game->battlefield); // We need to get the information about the cast method on both the card in the stack AND the card in play, //so we copy it from the previous card (in the stack) to the new one (in play). source->castMethod = castMethod; source->backupTargets = backupTgt; from = p->game->battlefield; } source->playerTarget = playerT; source->storedCard = oldStored; //Play SFX if (options[Options::SFXVOLUME].number > 0) { if(observer->getResourceManager()) observer->getResourceManager()->PlaySample(source->getSample()); } if(this->cost) { source->getManaCost()->setManaUsedToCast(NEW ManaCost(this->cost)); } AbilityFactory af(observer); af.addAbilities(observer->mLayers->actionLayer()->getMaxId(), this); return 1; }
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; }
//compute the difference between two mana costs ManaCost * ManaCost::Diff(ManaCost * _cost) { if (!_cost) return NEW ManaCost(*this); //diff with null is equivalent to diff with 0 vector<int16_t> diff; diff.resize((Constants::NB_Colors + 1) * 2); diff[Constants::NB_Colors * 2] = Constants::NB_Colors; for (int i = 0; i < Constants::NB_Colors; i++) { diff[i * 2] = i; diff[i * 2 + 1] = cost[i] - _cost->getCost(i); } int hybridResult = tryToPayHybrids(_cost->hybrids, _cost->hybrids.size(), diff); if (!hybridResult) randomDiffHybrids(_cost, diff); //Colorless mana, special case int colorless_idx = Constants::MTG_COLOR_ARTIFACT * 2 + 1; if (diff[colorless_idx] < 0) { for (int i = 0; i < Constants::NB_Colors; i++) { if (diff[i * 2 + 1] > 0) { if (diff[i * 2 + 1] + diff[colorless_idx] > 0) { diff[i * 2 + 1] += diff[colorless_idx]; diff[colorless_idx] = 0; break; } else { diff[colorless_idx] += diff[i * 2 + 1]; diff[i * 2 + 1] = 0; } } } } //Cost X if (_cost->hasX()) { diff[Constants::NB_Colors * 2 + 1] = 0; for (int i = 0; i < Constants::NB_Colors; i++) { if (diff[i * 2 + 1] > 0) { diff[Constants::NB_Colors * 2 + 1] += diff[i * 2 + 1]; diff[i * 2 + 1] = 0; } } } //cost x where x is specific. if (_cost->hasSpecificX()) { diff[Constants::NB_Colors * 2 + 1] = 0; if (diff[_cost->xColor * 2 + 1] > 0) { diff[Constants::NB_Colors * 2 + 1] += diff[_cost->xColor * 2 + 1]; diff[_cost->xColor * 2 + 1] = 0; } } ManaCost * result = NEW ManaCost(diff, Constants::NB_Colors + 1); return result; }
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; }
void ManaCost::copy(ManaCost * _manaCost) { if (!_manaCost) return; cost.erase(cost.begin() ,cost.end()); for (int i = 0; i <= Constants::NB_Colors; i++) { cost.push_back(_manaCost->getCost(i)); } hybrids = _manaCost->hybrids; SAFE_DELETE(extraCosts); if (_manaCost->extraCosts) { extraCosts = _manaCost->extraCosts->clone(); } SAFE_DELETE(kicker); if (_manaCost->kicker) { kicker = NEW ManaCost(); kicker->copy(_manaCost->kicker); kicker->isMulti = _manaCost->kicker->isMulti; } SAFE_DELETE(alternative); if (_manaCost->alternative) { alternative = NEW ManaCost(); alternative->copy(_manaCost->alternative); } SAFE_DELETE(BuyBack); if (_manaCost->BuyBack) { BuyBack = NEW ManaCost(); BuyBack->copy(_manaCost->BuyBack); } SAFE_DELETE(FlashBack); if (_manaCost->FlashBack) { FlashBack = NEW ManaCost(); FlashBack->copy(_manaCost->FlashBack); } SAFE_DELETE(Retrace); if (_manaCost->Retrace) { Retrace = NEW ManaCost(); Retrace->copy(_manaCost->Retrace); } SAFE_DELETE(morph); if (_manaCost->morph) { morph = NEW ManaCost(); morph->copy(_manaCost->morph); } SAFE_DELETE(suspend); if (_manaCost->suspend) { suspend = NEW ManaCost(); suspend->copy(_manaCost->suspend); } SAFE_DELETE(Bestow); if (_manaCost->Bestow) { Bestow = NEW ManaCost(); Bestow->copy(_manaCost->Bestow); } xColor = _manaCost->xColor; }