New game modes, bug fixes in primitives, improving AI, new planeswalkers type rule

New rules based on vanguard, 3 new random game modes, one is tribal and one uses any card in the game.

Several corrections and bugs fixes. Cards with x in their cost and that can target any player used to crash the game.

Teaching AI new things and changing values of efficiency.

You can have multiple "Jace" planeswalkers, you can't have two of the exact same name (no two Jace, the mind sculptor).
This commit is contained in:
Eduardo
2019-04-14 19:57:01 -05:00
parent 6eca5bea2f
commit ab1fbaa806
19 changed files with 623 additions and 3894 deletions
+146 -93
View File
@@ -46,14 +46,15 @@ int OrderedAIAction::getEfficiency(AADamager * aad)
return 0;
}
if(p == target->controller())
return 0;
if(p && target)
if(p == target->controller())
return 0;
if (dTarget && aad && (aad->getDamage() >= dTarget->toughness))
return 100;
if (dTarget && dTarget->toughness)
return (50 * aad->getDamage()) / dTarget->toughness;
return (10 * aad->getDamage()) / dTarget->toughness; // Don't waste damage spells
return 0;
}
@@ -129,8 +130,12 @@ int OrderedAIAction::getEfficiency()
{
efficiency = 95;
}
//TODO If the card is the target of a damage spell
break;
//TODO If the card is the target of a damage spell
if (!coreAbilityCardTarget->regenerateTokens && (MTGCardInstance*)target == a->source)
{
efficiency = 95;
}
break;
}
case MTGAbility::STANDARD_PREVENT:
{
@@ -271,7 +276,7 @@ int OrderedAIAction::getEfficiency()
}
case MTGAbility::STANDARD_PUMP:
{
efficiency = 0;
efficiency = 0;
if(!coreAbilityCardTarget)
break;
if(!target && !dynamic_cast<ALord*> (a) && (((MTGCardInstance *)a->source)->hasSubtype(Subtypes::TYPE_AURA) || ((MTGCardInstance *)a->source)->hasSubtype(Subtypes::TYPE_EQUIPMENT)))
@@ -291,9 +296,9 @@ int OrderedAIAction::getEfficiency()
int suggestion = af.abilityEfficiency(a, p, MODE_ABILITY);
//i do not set a starting eff. on this ability, this allows Ai to sometimes randomly do it as it normally does.
int currentPhase = g->getCurrentGamePhase();
if ((currentPhase == MTG_PHASE_COMBATBLOCKERS) || (currentPhase == MTG_PHASE_COMBATATTACKERS))
if (suggestion == BAKA_EFFECT_GOOD && target->controller() == p)
{
if (suggestion == BAKA_EFFECT_GOOD && target->controller() == p)
if ((currentPhase == MTG_PHASE_COMBATBLOCKERS) || (currentPhase == MTG_PHASE_COMBATATTACKERS))
{
if(coreAbilityCardTarget->defenser || coreAbilityCardTarget->blockers.size())
{
@@ -315,6 +320,10 @@ int OrderedAIAction::getEfficiency()
efficiency -= coreAbilityCardTarget->power;//we don't need to go overboard. better to not put all your eggs in a single basket.
}
}
if (currentPhase == MTG_PHASE_FIRSTMAIN)
{
efficiency = 10;
}
}
if (suggestion == BAKA_EFFECT_BAD && target->controller() != p && target->toughness > 0)
{
@@ -326,28 +335,41 @@ int OrderedAIAction::getEfficiency()
{
if(!coreAbilityCardTarget)
break;
// It used to skip most effects, with no other condition efficiency is -1
// Becomes is generally good so setting a value, but don't want to spam it
if (coreAbilityCardTarget && !coreAbilityCardTarget->isLand())
{
// Bonus if almost no cards in hand
if (p->game->hand->nb_cards <= 1)
{
efficiency = 60;
}
else efficiency = 30;
}
//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 (coreAbilityCardTarget && !coreAbilityCardTarget->isCreature() && currentPhase == MTG_PHASE_FIRSTMAIN)
{
efficiency = 100;
}
efficiency = 60;
}
break;
}
case MTGAbility::MANA_PRODUCER://only way to hit this condition is nested manaabilities, ai skips manaproducers by defualt when finding an ability to use.
{
AManaProducer * manamaker = dynamic_cast<AManaProducer*>(a);
GenericActivatedAbility * GAA = dynamic_cast<GenericActivatedAbility*>(ability);
AForeach * forMana = dynamic_cast<AForeach*>(GAA->ability);
if (manamaker && forMana)
{
int outPut = forMana->checkActivation();
if (ability->getCost() && outPut > int(ability->getCost()->getConvertedCost() +1) && currentPhase == MTG_PHASE_FIRSTMAIN && ability->source->controller()->game->hand->nb_cards > 1)
efficiency = 90;//might be a bit random, but better than never using them.
}
else
efficiency = 0;
break;
AManaProducer * manamaker = dynamic_cast<AManaProducer*>(a);
GenericActivatedAbility * GAA = dynamic_cast<GenericActivatedAbility*>(ability);
if(GAA)
{
AForeach * forMana = dynamic_cast<AForeach*>(GAA->ability);
if (manamaker && forMana)
{
int outPut = forMana->checkActivation();
if (ability->getCost() && outPut > int(ability->getCost()->getConvertedCost() +1) && currentPhase == MTG_PHASE_FIRSTMAIN && ability->source->controller()->game->hand->nb_cards > 1)
efficiency = 60;//might be a bit random, but better than never using them.
}
}
else
efficiency = 0;
break;
}
case MTGAbility::STANDARDABILITYGRANT:
{
@@ -426,11 +448,12 @@ int OrderedAIAction::getEfficiency()
case MTGAbility::LIFER:
{
//use life abilities whenever possible.
//use life abilities whenever possible. Well yes, but actually no
//limits mana and in case of Zuran Orb it just sacrifices all lands
AALifer * alife = (AALifer *) a;
Targetable * _t = alife->getTarget();
efficiency = 100;
efficiency = 80;
AbilityFactory af(g);
int suggestion = af.abilityEfficiency(a, p, MODE_ABILITY);
@@ -566,6 +589,12 @@ int OrderedAIAction::getEfficiency()
{
if (z == p->game->hand)
efficiency = 10 + (owner->getRandomGenerator()->random() % 10);//random chance to bounce their own card;
}
// We don't want to return cards in play to own hand, save rare combos
else if(target->currentZone == p->game->inPlay)
{
if (z == p->game->hand || z == p->game->library)
efficiency = (owner->getRandomGenerator()->random() % 10);//random chance to bounce their own card;
}
else
{
@@ -574,7 +603,11 @@ int OrderedAIAction::getEfficiency()
}
else
{
efficiency = 50;
// We don't want to return the ability source cards in play to own hand, save rare combos
// cards like Blinking Spirit use to be auto lose for AI
if(z == p->game->hand || z == p->game->library)
efficiency = 1;
else efficiency = 50;
//may abilities target the source until thier nested ability is activated, so 50% chance to use this
//mover, until we can come up with something more elegent....
}
@@ -639,7 +672,7 @@ int OrderedAIAction::getEfficiency()
{
AIPlayer * chk = (AIPlayer*)p;
if(may->ability && may->ability->getActionTc() && chk->chooseTarget(may->ability->getActionTc(),NULL,NULL,true))
efficiency = 50 + (owner->getRandomGenerator()->random() % 50);
efficiency = 80 + (owner->getRandomGenerator()->random() % 50);
}
if (p->game->hand->nb_cards == 0)
efficiency = (int) ((float) efficiency * 1.3); //increase chance of using ability if hand is empty
@@ -666,13 +699,18 @@ int OrderedAIAction::getEfficiency()
}
else if (dynamic_cast<MTGAlternativeCostRule *>(a))
{
efficiency += 55;
efficiency += 65;
}
else if (dynamic_cast<MTGSuspendRule *>(a))
{
efficiency += 55;
}
SAFE_DELETE(transAbility);
if (ability->source)
if(ability->source->hasType(Subtypes::TYPE_PLANESWALKER))
efficiency += 50;
SAFE_DELETE(transAbility);
return efficiency;
}
@@ -944,7 +982,7 @@ int OrderedAIAction::getRevealedEfficiency(MTGAbility * ability2)
//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 (coreAbilityCardTarget && !coreAbilityCardTarget->isCreature() && currentPhase == MTG_PHASE_FIRSTMAIN)
{
eff2 = 100;
eff2 = 70;
}
break;
}
@@ -957,7 +995,7 @@ int OrderedAIAction::getRevealedEfficiency(MTGAbility * ability2)
{
int outPut = forMana->checkActivation();
if (ability2->getCost() && outPut > int(ability2->getCost()->getConvertedCost() +1) && currentPhase == MTG_PHASE_FIRSTMAIN && ability2->source->controller()->game->hand->nb_cards > 1)
eff2 = 90;//might be a bit random, but better than never using them.
eff2 = 60;//might be a bit random, but better than never using them.
}
else
eff2 = 0;
@@ -2447,11 +2485,10 @@ int AIPlayerBaka::getEfficiency(MTGAbility * ability)
int AIPlayerBaka::selectMenuOption()
{
ActionLayer * object = observer->mLayers->actionLayer();
int doThis = -1;
int doThis = 0; // The AI just passes on things if set to -1, getEfficiency should be improved
if (object->menuObject)
{
int checkedLast = 0;
if(object->abilitiesMenu->isMultipleChoice && object->currentActionCard)
{
MenuAbility * currentMenu = NULL;
@@ -2510,7 +2547,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
{
nextCardToPlay = comboCards.back();
gotPayments.clear();
if((!pMana->canAfford(nextCardToPlay->getManaCost()) || nextCardToPlay->getManaCost()->getKicker()))
if((!pMana->canAfford(nextCardToPlay->getManaCost()) || nextCardToPlay->getManaCost()->getKicker() || nextCardToPlay->getManaCost()->getBestow()))
gotPayments = canPayMana(nextCardToPlay,nextCardToPlay->getManaCost());
DebugTrace("ai is doing a combo:" << nextCardToPlay->getName());
comboCards.pop_back();
@@ -2524,11 +2561,26 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
card = NULL;
gotPayments = vector<MTGAbility*>();
//canplayfromgraveyard
while ((card = cd.nextmatch(game->graveyard, card))&& card->has(Constants::CANPLAYFROMGRAVEYARD))
{
while ((card = cd.nextmatch(game->graveyard, card)))
{
bool hasFlashback = false;
if(card->getManaCost())
if(card->getManaCost()->getFlashback())
hasFlashback = true;
if( card->has(Constants::CANPLAYFROMGRAVEYARD) || card->has(Constants::TEMPFLASHBACK) || hasFlashback )
{
if (!CanHandleCost(card->getManaCost(),card))
continue;
if (hasFlashback && !CanHandleCost(card->getManaCost()->getFlashback(),card))
continue;
// Case were manacost is equal to flashback cost, if they are different the AI hangs
if (hasFlashback && (card->getManaCost() != card->getManaCost()->getFlashback()) )
continue;
if (card->hasType(Subtypes::TYPE_LAND))
{
if (game->playRestrictions->canPutIntoZone(card, game->inPlay) == PlayRestriction::CANT_PLAY)
@@ -2619,27 +2671,27 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
}
else
{
int shouldPlay = effectBadOrGood(card);
if (shouldPlay == BAKA_EFFECT_GOOD)
// Refactor to not check effect of lands since it always returned BAKA_EFFECT_DONTKNOW
// If it is a land, play it
if (card->isLand())
{
shouldPlayPercentage = 90;
}
else if (BAKA_EFFECT_DONTKNOW == shouldPlay)
{
//previously shouldPlayPercentage = 80;, I found this a little to high
//for cards which AI had no idea how to use.
shouldPlayPercentage = 60;
}
else if (card->isLand())
{
shouldPlayPercentage = 90;
}
else
{
// shouldPlay == baka_effect_bad giving it a 1 for odd ball lottery chance.
shouldPlayPercentage = 1;
}
}
else {
int shouldPlay = effectBadOrGood(card);
if (shouldPlay == BAKA_EFFECT_GOOD) {
shouldPlayPercentage = 90;
}
else if (BAKA_EFFECT_DONTKNOW == shouldPlay) {
//previously shouldPlayPercentage = 80;, I found this a little to high
//for cards which AI had no idea how to use.
shouldPlayPercentage = 60;
}
else {
// shouldPlay == baka_effect_bad giving it a 10 for odd ball lottery chance.
shouldPlayPercentage = 10;
}
}
}
//Reduce the chances of playing a spell with X cost if available mana is low
if (hasX)
@@ -2677,6 +2729,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
if (hasX)
maxCost = pMana->getConvertedCost();
}
}
}
//canplayfromexile
while ((card = cd.nextmatch(game->exile, card))&& card->has(Constants::CANPLAYFROMEXILE))
@@ -2774,27 +2827,27 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
}
else
{
int shouldPlay = effectBadOrGood(card);
if (shouldPlay == BAKA_EFFECT_GOOD)
// Refactor to not check effect of lands since it always returned BAKA_EFFECT_DONTKNOW
// If it is a land, play it
if (card->isLand())
{
shouldPlayPercentage = 90;
}
else if (BAKA_EFFECT_DONTKNOW == shouldPlay)
{
//previously shouldPlayPercentage = 80;, I found this a little to high
//for cards which AI had no idea how to use.
shouldPlayPercentage = 60;
}
else if (card->isLand())
{
shouldPlayPercentage = 90;
}
else
{
// shouldPlay == baka_effect_bad giving it a 1 for odd ball lottery chance.
shouldPlayPercentage = 1;
}
}
else {
int shouldPlay = effectBadOrGood(card);
if (shouldPlay == BAKA_EFFECT_GOOD) {
shouldPlayPercentage = 90;
}
else if (BAKA_EFFECT_DONTKNOW == shouldPlay) {
//previously shouldPlayPercentage = 80;, I found this a little to high
//for cards which AI had no idea how to use.
shouldPlayPercentage = 60;
}
else {
// shouldPlay == baka_effect_bad giving it a 10 for odd ball lottery chance.
shouldPlayPercentage = 10;
}
}
}
//Reduce the chances of playing a spell with X cost if available mana is low
if (hasX)
@@ -2928,27 +2981,27 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
}
else
{
int shouldPlay = effectBadOrGood(card);
if (shouldPlay == BAKA_EFFECT_GOOD)
// Refactor to not check effect of lands since it always returned BAKA_EFFECT_DONTKNOW
// If it is a land, play it
if (card->isLand())
{
shouldPlayPercentage = 90;
}
else if (BAKA_EFFECT_DONTKNOW == shouldPlay)
{
//previously shouldPlayPercentage = 80;, I found this a little to high
//for cards which AI had no idea how to use.
shouldPlayPercentage = 60;
}
else if (card->isLand())
{
shouldPlayPercentage = 90;
}
else
{
// shouldPlay == baka_effect_bad giving it a 1 for odd ball lottery chance.
shouldPlayPercentage = 1;
}
}
else {
int shouldPlay = effectBadOrGood(card);
if (shouldPlay == BAKA_EFFECT_GOOD) {
shouldPlayPercentage = 90;
}
else if (BAKA_EFFECT_DONTKNOW == shouldPlay) {
//previously shouldPlayPercentage = 80;, I found this a little to high
//for cards which AI had no idea how to use.
shouldPlayPercentage = 60;
}
else {
// shouldPlay == baka_effect_bad giving it a 10 for odd ball lottery chance.
shouldPlayPercentage = 10;
}
}
}
//Reduce the chances of playing a spell with X cost if available mana is low
if (hasX)
+1 -1
View File
@@ -463,7 +463,7 @@ void CardGui::Render()
string buff = "";
string starMark = "";
if(card->exerted)
starMark += "*";
starMark += "exerted";
if(card->isToken && !card->isACopier)
buff = "T";
if(card->isToken && card->isACopier)
+5 -4
View File
@@ -417,10 +417,11 @@ ManaCost* CardPrimitive::getManaCost()
bool CardPrimitive::hasType(int _type)
{
for (size_t i = 0; i < types.size(); i++)
if (types[i] == _type)
return true;
return false;
if (types.size() > 400) {return false;} // Null pointer?
for (size_t i = 0; i < types.size(); i++)
if (types[i] == _type)
return true;
return false;
}
bool CardPrimitive::hasSubtype(int _subtype)
+4 -1
View File
@@ -770,7 +770,10 @@ void GameStateDuel::Update(float dt)
musictrack = "ai_baka_music_momir.mp3";
else if (mParent->gameType == GAME_TYPE_RANDOM1 || mParent->gameType == GAME_TYPE_RANDOM2) musictrack
= "ai_baka_music_random.mp3";
else if (mParent->gameType == GAME_TYPE_RANDOM3 || mParent->gameType == GAME_TYPE_RANDOM5) musictrack
= "ai_baka_music_random.mp3";
else if (mParent->gameType == GAME_TYPE_HORDE) musictrack
= "ai_baka_music_momir.mp3";
if (!MusicExist(musictrack))
musictrack = "ai_baka_music.mp3";
+1 -1
View File
@@ -312,7 +312,7 @@ void GuiPlay::Render()
MTGCardInstance * ctarget = ((MTGCardInstance *)(*it)->card->isAttacking);
if(ctarget->hasType(Subtypes::TYPE_PLANESWALKER) && observer->isInPlay(ctarget) && observer->getCurrentGamePhase() < MTG_PHASE_COMBATEND)
{
JRenderer::GetInstance()->DrawLine((*it)->actX,(*it)->actY,ctarget->view->actX,ctarget->view->actY,0.5f,ARGB(128 - wave, 255, 20, 0));
JRenderer::GetInstance()->DrawLine((*it)->actX,(*it)->actY,ctarget->view->actX,ctarget->view->actY,0.5f,ARGB(128 - wave, 255, 40, 40));
}
}
}
+45 -9
View File
@@ -1835,7 +1835,8 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
tc->targetter = NULL;
}
else
tc->targetter->bypassTC = false;
if (tc->targetter)
tc->targetter->bypassTC = false;
sWithoutTc = splitTarget[0];
sWithoutTc.append(splitTarget[2]);
}
@@ -1935,7 +1936,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
{
if (!(spell && spell->FullfilledAlternateCost(kAlternateCostIds[i])))
{
DebugTrace("INFO parseMagicLine: Alternative Cost was not fulfilled for " << s);
DebugTrace("INFO parseMagicLine: Alternative Cost was not fulfilled for " << spell << s);
SAFE_DELETE(tc);
return NULL;
}
@@ -3512,7 +3513,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
//produce additional mana when a mana is engaged
if (s.find("producecolor:") != string::npos)
{
{
return NEW AEngagedManaAbility(observer, id, card,s.substr(13));
}
@@ -4094,13 +4095,14 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
return NEW ANewAffinity(observer, id, card, tcString, manaString);
}
//proliferate
//proliferate, rule changes in War of the Spark set
found = s.find("proliferate");
if (found != string::npos)
{
MTGAbility * a = NEW AAProliferate(observer, id, card, target);
a->oneShot = 1;
a->canBeInterrupted = false;
((AAProliferate*)a)->allcounters = true;
return a;
}
//proliferate all counters
@@ -4545,6 +4547,8 @@ int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, Targ
return BAKA_EFFECT_BAD;
if (dynamic_cast<AManaProducer *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<AARemoveAllCounter *> (a))
return BAKA_EFFECT_BAD;
// Equipment that gets immediately attached. Todo: check the abilities associated with Equip, to make sure they're good (for now it seems to be the majority of the cases)?
if (dynamic_cast<AEquip *> (a))
@@ -4568,12 +4572,12 @@ int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, Targ
if (AAMover * aam = dynamic_cast<AAMover *>(a))
{
MTGGameZone * z = aam->destinationZone(target);
if (tc && tc->targetsZone(p->game->library))
if (tc && tc->targetsZone(p->game->library)||tc && tc->targetsZone(p->game->graveyard))
{
if (z == p->game->hand || z == p->game->inPlay)
return BAKA_EFFECT_GOOD;
}
return BAKA_EFFECT_BAD; //TODO
return BAKA_EFFECT_DONTKNOW; //TODO
}
if (dynamic_cast<AACopier *> (a))
@@ -4598,6 +4602,31 @@ int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, Targ
return BAKA_EFFECT_GOOD;
if (dynamic_cast<ABushidoAbility *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<AACascade *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<AACastCard *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<AAFlip *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<AAImprint *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<ABestow *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<AExert *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<ALoseAbilities *> (a))
return BAKA_EFFECT_BAD;
if (dynamic_cast<AModularAbility *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<APaired *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<AProduceMana *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<AACloner *> (a))
return BAKA_EFFECT_GOOD;
if (dynamic_cast<AAModTurn *> (a))
return BAKA_EFFECT_GOOD;
if (PTInstant * abi = dynamic_cast<PTInstant *>(a))
return (abi->wppt->power.getValue() >= 0 && abi->wppt->toughness.getValue() >= 0) ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD;
if (APowerToughnessModifier * abi = dynamic_cast<APowerToughnessModifier *>(a))
@@ -4628,6 +4657,8 @@ int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, Targ
badAbilities[(int)Constants::NOLIFEGAINOPPONENT] = true;
badAbilities[(int)Constants::MUSTBLOCK] = true;
badAbilities[(int)Constants::FLYERSONLY] = true;
badAbilities[(int)Constants::TREASON] = true;
badAbilities[(int)Constants::SHACKLER] = true;
if (AInstantBasicAbilityModifierUntilEOT * abi = dynamic_cast<AInstantBasicAbilityModifierUntilEOT *>(a))
{
@@ -4694,7 +4725,7 @@ int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCar
card->exileEffects = true;
break;
}
if (dest == zones->library)
if (dest == zones->library)
{
magicText = card->magicTexts["library"];
break;
@@ -4788,7 +4819,7 @@ int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCar
}
else
{
DebugTrace("ABILITYFACTORY ERROR: Parser returned NULL");
DebugTrace("ABILITYFACTORY ERROR: Parser returned NULL " + magicText);
}
}
return result;
@@ -4804,7 +4835,7 @@ int AbilityFactory::getAbilities(vector<MTGAbility *> * v, Spell * spell, MTGCar
* - doTap (a dirty way to know if tapping is included in the cost...
*/
int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int mode, TargetChooser * tc, MTGGameZone * dest)
{
{try{
int dryMode = 0;
if (!spell && !dest)
dryMode = 1;
@@ -4888,6 +4919,11 @@ int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int
}
return result;
}
catch(exception) {
DebugTrace("MAGIC TEST ERROR: Parser returned NULL");
}
return 0;
}
void AbilityFactory::addAbilities(int _id, Spell * spell)
+52 -20
View File
@@ -505,6 +505,20 @@ int MTGPutInPlayRule::reactToClick(MTGCardInstance * card)
}
delete withKickerCost;
}
if (card->getManaCost()->getBestow())
{
ManaCost * withBestowCost= NEW ManaCost(card->getManaCost());
withBestowCost->add(withBestowCost->getBestow());
DebugTrace("AltCost BESTOW " << withBestowCost);
if (previousManaPool->canAfford(withBestowCost))
{
player->getManaPool()->pay(card->getManaCost()->getBestow());
payResult = ManaCost::MANA_PAID_WITH_BESTOW;
}
delete withBestowCost;
}
card->getManaCost()->doPayExtra();
ManaCost * spellCost = previousManaPool->Diff(player->getManaPool());
@@ -652,6 +666,19 @@ int MTGKickerRule::reactToClick(MTGCardInstance * card)
payResult = ManaCost::MANA_PAID_WITH_KICKER;
}
delete withKickerCost;
}
// Handles bestow,also has to go in isExtraPaymentSet
if (card->getManaCost()->getBestow())
{
ManaCost * withBestowCost= NEW ManaCost(card->getManaCost());
withBestowCost->add(withBestowCost->getBestow());
if (previousManaPool->canAfford(withBestowCost))
{
player->getManaPool()->pay(card->getManaCost()->getBestow());
payResult = ManaCost::MANA_PAID_WITH_BESTOW;
}
delete withBestowCost;
}
card->getManaCost()->doPayExtra();
ManaCost * spellCost = previousManaPool->Diff(player->getManaPool());
@@ -3486,7 +3513,7 @@ PermanentAbility(observer, _id)
}
int MTGNewPlaneswalker::receiveEvent(WEvent * e)
{
{
if(game->getCurrentTargetChooser() || game->mLayers->actionLayer()->isWaitingForAnswer())
return 0;
if (WEventZoneChange* ev1 = dynamic_cast<WEventZoneChange*>(e))
@@ -3494,7 +3521,7 @@ int MTGNewPlaneswalker::receiveEvent(WEvent * e)
if (ev1->to == game->players[0]->game->inPlay || ev1->to == game->players[1]->game->inPlay)
{
MTGCardInstance * card = ev1->card;
if(card && card->hasType(Subtypes::TYPE_PLANESWALKER))
if(card && card->countDuplicateCardNames() > 1 && card->hasType(Subtypes::TYPE_LEGENDARY))
{
CheckPW(card);
return 1;
@@ -3504,7 +3531,7 @@ int MTGNewPlaneswalker::receiveEvent(WEvent * e)
else if(WEventCardControllerChange* ev2 = dynamic_cast<WEventCardControllerChange*>(e))
{
MTGCardInstance * card = ev2->card;
if(card && card->hasType(Subtypes::TYPE_PLANESWALKER))
if(card && card->countDuplicateCardNames() > 1 && card->hasType(Subtypes::TYPE_LEGENDARY))
{
CheckPW(card);
return 1;
@@ -3513,7 +3540,7 @@ int MTGNewPlaneswalker::receiveEvent(WEvent * e)
else if(WEventCardTransforms* ev3 = dynamic_cast<WEventCardTransforms*>(e))
{
MTGCardInstance * card = ev3->card;
if(card && card->hasType(Subtypes::TYPE_PLANESWALKER))
if(card && card->countDuplicateCardNames() > 1 && card->hasType(Subtypes::TYPE_LEGENDARY))
{
CheckPW(card);
return 1;
@@ -3522,7 +3549,7 @@ int MTGNewPlaneswalker::receiveEvent(WEvent * e)
else if(WEventCardCopiedACard* ev4 = dynamic_cast<WEventCardCopiedACard*>(e))
{
MTGCardInstance * card = ev4->card;
if(card && card->hasType(Subtypes::TYPE_PLANESWALKER))
if(card && card->countDuplicateCardNames() > 1 && card->hasType(Subtypes::TYPE_LEGENDARY))
{
CheckPW(card);
return 1;
@@ -3531,7 +3558,7 @@ int MTGNewPlaneswalker::receiveEvent(WEvent * e)
else if(WEventCardFaceUp* ev5 = dynamic_cast<WEventCardFaceUp*>(e))
{
MTGCardInstance * card = ev5->card;
if(card && card->hasType(Subtypes::TYPE_PLANESWALKER))
if(card && card->countDuplicateCardNames() > 1 && card->hasType(Subtypes::TYPE_LEGENDARY))
{
CheckPW(card);
return 1;
@@ -3540,7 +3567,7 @@ int MTGNewPlaneswalker::receiveEvent(WEvent * e)
else if(WEventCardPhasesIn* ev6 = dynamic_cast<WEventCardPhasesIn*>(e))
{
MTGCardInstance * card = ev6->card;
if(card && card->hasType(Subtypes::TYPE_PLANESWALKER))
if(card && card->countDuplicateCardNames() > 1 && card->hasType(Subtypes::TYPE_LEGENDARY))
{
CheckPW(card);
return 1;
@@ -3550,23 +3577,26 @@ int MTGNewPlaneswalker::receiveEvent(WEvent * e)
}
int MTGNewPlaneswalker::CheckPW(MTGCardInstance * card)
{
if(!card)
if(!card)
return 0;
if(card->isPhased)
return 0;
if(card->countDuplicateCardTypes() < 1)
if (card->hasType(Subtypes::TYPE_LEGENDARY) && card->controller()->game->inPlay->hasCard(card))
if(card->has(Constants::NOLEGEND)||card->controller()->opponent()->inPlay()->hasAbility(Constants::NOLEGENDRULE)||card->controller()->inPlay()->hasAbility(Constants::NOLEGENDRULE))
return 0;
if(card->countDuplicateCardNames() <= 2)
return 0;
MovePW(card);
return 1;
}
void MTGNewPlaneswalker::MovePW(MTGCardInstance * card)
{
{
game->LPWeffect = true;
vector<MTGAbility*>selection;
MTGCardInstance * myClone = NEW MTGCardInstance(card, card->controller()->game);
TargetChooserFactory tfL(game);
tcP = tfL.createTargetChooser("planeswalker[share!types!]|mybattlefield",myClone);
tcP = tfL.createTargetChooser("*[share!name!]|mybattlefield",myClone);
tcP->targetter = NULL;
tcP->maxtargets = 1;
PWrule = NEW AAMover(game, game->mLayers->actionLayer()->getMaxId(), myClone, NULL,"ownergraveyard","Put in Graveyard");
@@ -3679,24 +3709,27 @@ ListMaintainerAbility(observer, _id)
int MTGPlaneWalkerRule::canBeInList(MTGCardInstance * card)
{
if(card->isPhased)
if(card->isPhased)
return 0;
if (card->hasType(Subtypes::TYPE_PLANESWALKER) && card->controller()->game->inPlay->hasCard(card))
if (card->hasType(Subtypes::TYPE_LEGENDARY) && card->controller()->game->inPlay->hasCard(card))
{
return 1;
if(card->has(Constants::NOLEGEND)||card->controller()->opponent()->inPlay()->hasAbility(Constants::NOLEGENDRULE)||card->controller()->inPlay()->hasAbility(Constants::NOLEGENDRULE))
return 0;
else
return 1;
}
return 0;
}
int MTGPlaneWalkerRule::added(MTGCardInstance * card)
{
map<MTGCardInstance *, bool>::iterator it;
map<MTGCardInstance *, bool>::iterator it;
int destroy = 0;
vector<MTGCardInstance*>oldCards;
for (it = cards.begin(); it != cards.end(); it++)
{
MTGCardInstance * comparison = (*it).first;
if (comparison != card && comparison->types == card->types && comparison->controller() == card->controller())
if (comparison != card && comparison->controller() == card->controller() && !(comparison->getName().compare(card->getName())))
if (!(game->getCurrentTargetChooser() || game->mLayers->actionLayer()->isWaitingForAnswer()))
{
oldCards.push_back(comparison);
@@ -3704,11 +3737,10 @@ int MTGPlaneWalkerRule::added(MTGCardInstance * card)
game->LPWeffect = true;
}
}
if (destroy)
if(destroy)
{
vector<MTGAbility*>selection;
MultiAbility * multi = NEW MultiAbility(game,game->mLayers->actionLayer()->getMaxId(), card, card, NULL);
MultiAbility * multi = NEW MultiAbility(game, game->mLayers->actionLayer()->getMaxId(), card, card, NULL);
for(unsigned int i = 0;i < oldCards.size();i++)
{
AAMover *a = NEW AAMover(game, game->mLayers->actionLayer()->getMaxId(), card, oldCards[i],"ownergraveyard","Keep New");
@@ -3722,7 +3754,7 @@ int MTGPlaneWalkerRule::added(MTGCardInstance * card)
b->oneShot = true;
MTGAbility * b1 = b;
selection.push_back(b1);
MTGAbility * menuChoice = NEW MenuAbility(game, game->mLayers->actionLayer()->getMaxId(), card, card,true,selection,card->controller(),"Planeswalker Rule");
MTGAbility * menuChoice = NEW MenuAbility(game, game->mLayers->actionLayer()->getMaxId(), card, card,true,selection,card->controller(),"Legendary Rule");
menuChoice->addToGame();
}
return 1;
+3 -1
View File
@@ -579,6 +579,8 @@ int ManaCost::hasAnotherCost()
if(kicker)
result = 1;
//kicker is the only one ai knows for now, later hasAnotherCost() can be used to determine other cost types.
if(Retrace || BuyBack || alternative || FlashBack || morph || suspend || Bestow)
result = 1;
return result;
}
@@ -730,7 +732,7 @@ int ManaCost::getCost(int color)
{
if (cost.size() <= (size_t)color)
{
DebugTrace("Seems ManaCost was not properly initialized");
DebugTrace("in GetCost Seems ManaCost was not properly initialized");
return 0;
}
return cost[color];
+164 -1
View File
@@ -168,6 +168,10 @@ void Rules::addExtraRules(GameObserver* g)
difficultyRating = 0;
else if(g->mRules->gamemode == GAME_TYPE_RANDOM1 || g->mRules->gamemode == GAME_TYPE_RANDOM2)
difficultyRating = 0;
else if(g->mRules->gamemode == GAME_TYPE_RANDOM3 || g->mRules->gamemode == GAME_TYPE_RANDOM5)
difficultyRating = 0;
else if(g->mRules->gamemode == GAME_TYPE_HORDE)
difficultyRating = 0;
else if (g->mRules->gamemode == GAME_TYPE_STORY)
difficultyRating = 0;
else if (a->aType == MTGAbility::STANDARD_DRAW)
@@ -196,9 +200,11 @@ void Rules::addExtraRules(GameObserver* g)
a->resolve();
else if(g->mRules->gamemode == GAME_TYPE_RANDOM1 || g->mRules->gamemode == GAME_TYPE_RANDOM2)
a->resolve();
else if(g->mRules->gamemode == GAME_TYPE_RANDOM3 || g->mRules->gamemode == GAME_TYPE_RANDOM5)
a->resolve();
else if (g->mRules->gamemode == GAME_TYPE_STORY)
a->resolve();
else//stupid protections to keep this out of mimor and other game modes.
else//stupid protections to keep this out of momir and other game modes.
{
handsize = ((AADrawer *)a)->getNumCards();
if(difficultyRating == EASY)
@@ -305,6 +311,154 @@ Player * Rules::loadPlayerRandom(GameObserver* observer, int isAI, int mode)
return player;
}
Player * Rules::loadPlayerRandomThree(GameObserver* observer, int isAI)
{
int color1 = 1 + observer->getRandomGenerator()->random() % 5;
int color2 = 1 + observer->getRandomGenerator()->random() % 5;
int color3 = 1 + observer->getRandomGenerator()->random() % 5;
int color0 = Constants::MTG_COLOR_ARTIFACT;
int colors[] = { color1, color2, color3, color0 };
int nbcolors = 4;
string lands[] = { "", "forest", "island", "mountain", "swamp", "plains", "" };
MTGDeck * tempDeck = NEW MTGDeck(MTGCollection());
tempDeck->addRandomCards(1, 0, 0, -1, lands[color1].c_str());
tempDeck->addRandomCards(1, 0, 0, -1, lands[color2].c_str());
tempDeck->addRandomCards(1, 0, 0, -1, lands[color3].c_str());
tempDeck->addRandomCards(6, 0, 0, 'R', lands[color1].c_str());
tempDeck->addRandomCards(6, 0, 0, 'R', lands[color2].c_str());
tempDeck->addRandomCards(6, 0, 0, 'R', lands[color3].c_str());
tempDeck->addRandomCards(3, 0, 0, -1, "land");
tempDeck->addRandomCards(1, 0, 0, 'U', "land");
tempDeck->addRandomCards(1, 0, 0, 'R', "land");
tempDeck->addRandomCards(18, 0, 0, -1, "creature", colors, nbcolors);
tempDeck->addRandomCards(1, 0, 0, 'R', "creature", colors, nbcolors);
tempDeck->addRandomCards(1, 0, 0, 'M', "creature", colors, nbcolors);
tempDeck->addRandomCards(3, 0, 0, -1, "sorcery", colors, nbcolors);
tempDeck->addRandomCards(3, 0, 0, -1, "enchantment", colors, nbcolors);
tempDeck->addRandomCards(3, 0, 0, -1, "instant", colors, nbcolors);
tempDeck->addRandomCards(4, 0, 0, -1, "artifact", colors, nbcolors);
tempDeck->addRandomCards(1, 0, 0, -1, "planeswalker", colors, nbcolors);
string deckFile = "random";
string deckFileSmall = "random";
Player *player = NULL;
if (!isAI) // Human Player
player = NEW HumanPlayer(observer, deckFile, deckFileSmall, false, tempDeck);
else
player = NEW AIPlayerBaka(observer, deckFile, deckFileSmall, "", tempDeck);
return player;
}
Player * Rules::loadPlayerRandomFive(GameObserver* observer, int isAI)
{
MTGDeck * tempDeck = NEW MTGDeck(MTGCollection());
tempDeck->addRandomCards(1, 0, 0, -1, "plains");
tempDeck->addRandomCards(1, 0, 0, -1, "island");
tempDeck->addRandomCards(1, 0, 0, -1, "swamp");
tempDeck->addRandomCards(1, 0, 0, -1, "mountain");
tempDeck->addRandomCards(1, 0, 0, -1, "forest");
tempDeck->addRandomCards(3, 0, 0, 'R', "plains");
tempDeck->addRandomCards(3, 0, 0, 'R', "island");
tempDeck->addRandomCards(3, 0, 0, 'R', "swamp");
tempDeck->addRandomCards(3, 0, 0, 'R', "mountain");
tempDeck->addRandomCards(3, 0, 0, 'R', "forest");
tempDeck->addRandomCards(4, 0, 0, -1, "land");
tempDeck->addRandomCards(1, 0, 0, 'U', "land");
tempDeck->addRandomCards(1, 0, 0, 'R', "land");
tempDeck->addRandomCards(18, 0, 0, -1, "creature");
tempDeck->addRandomCards(1, 0, 0, 'R', "creature");
tempDeck->addRandomCards(1, 0, 0, 'M', "creature");
tempDeck->addRandomCards(3, 0, 0, -1, "sorcery");
tempDeck->addRandomCards(3, 0, 0, -1, "enchantment");
tempDeck->addRandomCards(3, 0, 0, -1, "instant");
tempDeck->addRandomCards(4, 0, 0, -1, "artifact");
tempDeck->addRandomCards(1, 0, 0, -1, "planeswalker");
string deckFile = "random";
string deckFileSmall = "random";
Player *player = NULL;
if (!isAI) // Human Player
player = NEW HumanPlayer(observer, deckFile, deckFileSmall, false, tempDeck);
else
player = NEW AIPlayerBaka(observer, deckFile, deckFileSmall, "", tempDeck);
return player;
}
Player * Rules::loadPlayerHorde(GameObserver* observer, int isAI)
{
int nbColors = 1;
string randomTribe = "";
int tribeColor[] = { observer->getRandomGenerator()->random() % 6 };
string lands[] = { "land", "forest", "island", "mountain", "swamp", "plains" };
const char* const multicolorTribes[] = { "Ally", "Atog", "Eldrazi", "Elemental", "Human", "Knight",
"Myr", "Samurai", "Shaman", "Shapeshifter", "Sliver", "Soldier", "Spellshaper" };
const char* const whiteTribes[] = { "Angel","Cat", "Griffin", "Kithkin", "Knight", "Pegasus", "Soldier" };
const char* const blueTribes[] = { "Artificer", "Merfolk", "Drake", "Faerie", "Illusion", "Vedalken" };
const char* const blackTribes[] = { "Demon", "Faerie", "Horror", "Pirate", "Rat", "Shade", "Skeleton", "Vampire", "Zombie" };
const char* const redTribes[] = { "Barbarian", "Berserker","Cat", "Devil", "Goblin", "Minotaur", "Werewolf" };
const char* const greenTribes[] = { "Beast", "Cat", "Centaur", "Dinosaur", "Druid", "Elf", "Fungus",
"Saproling", "Spider", "Treefolk", "Werewolf" };
int multicolorTribesSize = sizeof(multicolorTribes)/sizeof(multicolorTribes[0]);
int whiteTribesSize = sizeof(whiteTribes)/sizeof(whiteTribes[0]);
int blueTribesSize = sizeof(blueTribes)/sizeof(blueTribes[0]);
int blackTribesSize = sizeof(blackTribes)/sizeof(blackTribes[0]);
int redTribesSize = sizeof(redTribes)/sizeof(redTribes[0]);
int greenTribesSize = sizeof(greenTribes)/sizeof(greenTribes[0]);
switch (tribeColor[0])
{
case Constants::MTG_COLOR_ARTIFACT :
randomTribe = multicolorTribes[observer->getRandomGenerator()->random() % multicolorTribesSize];
nbColors = 0;
break;
case Constants::MTG_COLOR_WHITE :
randomTribe = whiteTribes[observer->getRandomGenerator()->random() % whiteTribesSize];
break;
case Constants::MTG_COLOR_BLUE :
randomTribe = blueTribes[observer->getRandomGenerator()->random() % blueTribesSize];
break;
case Constants::MTG_COLOR_BLACK :
randomTribe = blackTribes[observer->getRandomGenerator()->random() % blackTribesSize];
break;
case Constants::MTG_COLOR_RED :
randomTribe = redTribes[observer->getRandomGenerator()->random() % redTribesSize];
break;
case Constants::MTG_COLOR_GREEN :
randomTribe = greenTribes[observer->getRandomGenerator()->random() % greenTribesSize];
break;
}
MTGDeck * tempDeck = NEW MTGDeck(MTGCollection());
tempDeck->addRandomCards(16, 0, 0, -1, lands[tribeColor[0]].c_str());
tempDeck->addRandomCards(4, 0, 0, 'R', lands[tribeColor[0]].c_str());
tempDeck->addRandomCards(4, 0, 0, -1, "land");
tempDeck->addRandomCards(21, 0, 0, -1, randomTribe);
tempDeck->addRandomCards(5, 0, 0, -1, "enchantment", tribeColor, nbColors);
tempDeck->addRandomCards(5, 0, 0, -1, "instant", tribeColor, nbColors);
tempDeck->addRandomCards(5, 0, 0, -1, "sorcery", tribeColor, nbColors);
string deckFile = "random";
string deckFileSmall = "random";
Player *player = NULL;
if (!isAI) // Human Player
player = NEW HumanPlayer(observer, deckFile, deckFileSmall, false, tempDeck);
else
player = NEW AIPlayerBaka(observer, deckFile, deckFileSmall, "", tempDeck);
return player;
}
Player * Rules::initPlayer(GameObserver *g, int playerId)
{
Player * p = g->players.size() > 1?g->players[playerId]:NULL;
@@ -322,6 +476,12 @@ Player * Rules::initPlayer(GameObserver *g, int playerId)
return loadPlayerRandom(g, isAI, GAME_TYPE_RANDOM1);
case GAME_TYPE_RANDOM2:
return loadPlayerRandom(g, isAI, GAME_TYPE_RANDOM2);
case GAME_TYPE_RANDOM3:
return loadPlayerRandomThree(g, isAI);
case GAME_TYPE_RANDOM5:
return loadPlayerRandomFive(g, isAI);
case GAME_TYPE_HORDE:
return loadPlayerHorde(g, isAI);
default:
return NULL;
}
@@ -627,6 +787,9 @@ GameType Rules::strToGameMode(string s)
if (s.compare("momir") == 0) return GAME_TYPE_MOMIR;
if (s.compare("random1") == 0) return GAME_TYPE_RANDOM1;
if (s.compare("random2") == 0) return GAME_TYPE_RANDOM2;
if (s.compare("random3") == 0) return GAME_TYPE_RANDOM3;
if (s.compare("random5") == 0) return GAME_TYPE_RANDOM5;
if (s.compare("horde") == 0) return GAME_TYPE_HORDE;
if (s.compare("story") == 0) return GAME_TYPE_STORY;
if (s.compare("stonehewer") == 0) return GAME_TYPE_STONEHEWER;
if (s.compare("hermit") == 0) return GAME_TYPE_HERMIT;