From 3dfcc65fa6c7295c635d2652178918ba6d41851f Mon Sep 17 00:00:00 2001 From: Eduardo Date: Wed, 17 Apr 2019 20:28:43 -0500 Subject: [PATCH] Bug fix for the biggest crash and fixes to the primitives I figured a while ago that the cause of the crash was a rendering issue, not an engine issue, since you could copy spells in the stack if you didn't targeted them. The crash was most likely in ActionStack, just had to follow the trace. It doesn't affect anithing else, that line was trying to render something in the stack that's a permanent but it always had bad ptrs. --- projects/mtg/bin/Res/sets/primitives/mtg.txt | 62 ++-- projects/mtg/src/AIPlayerBaka.cpp | 337 ++++++++++--------- projects/mtg/src/ActionStack.cpp | 4 +- projects/mtg/src/MTGAbility.cpp | 4 + projects/mtg/src/MTGCardInstance.cpp | 10 +- 5 files changed, 221 insertions(+), 196 deletions(-) diff --git a/projects/mtg/bin/Res/sets/primitives/mtg.txt b/projects/mtg/bin/Res/sets/primitives/mtg.txt index 95f4d67d3..6a8e21425 100644 --- a/projects/mtg/bin/Res/sets/primitives/mtg.txt +++ b/projects/mtg/bin/Res/sets/primitives/mtg.txt @@ -1534,6 +1534,7 @@ target=creature[attacking;blocking] auto=moveTo(ownerlibrary) auto=alternative moveTo(ownerlibrary) target(creature[attacking;blocking]) other={3}{WU}{T(creature[blue;white]|mybattlefield)}{T(creature[blue;white]|mybattlefield)} name(Cast with Conspire) +otherrestriction=type(creature[blue;white]|myBattlefield)~morethan~1 text=Put target attacking or blocking creature on top of its owner's library. -- Conspire (As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.) mana={3}{WU} type=Instant @@ -2484,7 +2485,7 @@ toughness=1 name=Alabaster Potion auto=choice life:X target(player) auto=choice prevent:X target(creature,player) -text=Choose one - Target player gains X life; or prevent the next X damage that would be dealt to target creature or player this turn. +text=Choose one Target player gains X life. Prevent the next X damage that would be dealt to any target this turn. mana={X}{W}{W} type=Instant [/card] @@ -9234,6 +9235,7 @@ target=creature auto=2/2 auto=alternative target(creature) 2/2 other={GW}{T(creature[green;white]|mybattlefield)}{T(creature[green;white]|mybattlefield)} name(Conspire) +otherrestriction=type(creature[green;white]|myBattlefield)~morethan~1 text=Target creature gets +2/+2 until end of turn. -- Conspire (As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.) mana={GW} type=Instant @@ -11395,8 +11397,7 @@ toughness=3 [/card] [card] name=Blaze -target=creature,player -auto=damage:X +auto=damage:X target(creature,player) text=Blaze deals X damage to target creature or player. mana={X}{R} type=Sorcery @@ -15272,8 +15273,7 @@ toughness=2 [/card] [card] name=Brood Birthing -auto=aslongas(eldrazi spawn|myBattlefield) token(Eldrazi Spawn,Creature Eldrazi Spawn,0/1) and!( transforms((,newability[{S}:Add{1}])) forever )!*2 -auto=token(Eldrazi Spawn,Creature Eldrazi Spawn,0/1) and!( transforms((,newability[{S}:Add{1}])) forever )! +auto=aslongas(eldrazi spawn|myBattlefield) token(Eldrazi Spawn,Creature Eldrazi Spawn,0/1) and!( transforms((,newability[{S}:Add{1}])) forever )!*3 text=If you control at least one 0/1 Eldrazi Spawn creature token, put three 0/1 colorless Eldrazi Spawn creature tokens onto the battlefield. They have "Sacrifice this creature: Add {1} to your mana pool." mana={1}{R} type=Sorcery @@ -15281,7 +15281,7 @@ type=Sorcery [card] name=Brood Butcher auto=choice token(Eldrazi Scion,Creature Eldrazi Scion,1/1) and!( transforms((,newability[{S}:Add{C}])) forever )! controller -auto={B}{G}{S(creature|mybattlefield):target(creature) -2/-2 ueot +auto={B}{G}{S(creature|mybattlefield)}:target(creature) -2/-2 ueot text=Devoid (This card has no color.) -- When Brood Butcher enters the battlefield, put a 1/1 colorless Eldrazi Scion creature token onto the battlefield. It has "Sacrifice this creature: Add {1} to your mana pool." -- {B}{G}, Sacrifice a creature: Target creature gets -2/-2 until end of turn. mana={3}{B}{G} abilities=devoid @@ -15790,6 +15790,7 @@ target=creature,player auto=damage:3 auto=alternative damage:3 target(creature,player) other={3}{R}{T(creature[red]|mybattlefield)}{T(creature[red]|mybattlefield)} name(pay Conspire) +otherrestriction=type(creature[red]|myBattlefield)~morethan~1 text=Burn Trail deals 3 damage to target creature or player. -- Conspire (As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.) mana={3}{R} type=Sorcery @@ -17622,7 +17623,7 @@ type=Enchantment [/card] [card] name=Cathar's Shield -auto=teach(creature) vigilence +auto=teach(creature) vigilance auto=teach(creature) 0/3 auto={3}:equip text=Equipped creature gets +0/+3 and has vigilance. -- Equip {3} ({3}: Attach to target creature you control. Equip only as a sorcery.) @@ -22166,8 +22167,7 @@ type=Sorcery [/card] [card] name=Consume Spirit -target=creature,player -auto=damage:castx +auto=damage:castx target(creature,player) auto=life:castx controller text=Spend only black mana on X. -- Consume Spirit deals X damage to target creature or player and you gain X life. mana={1}{B}{X:black} @@ -23482,8 +23482,8 @@ type=Sorcery [card] name=Crater's Claws target=creature,player -auto=if type(creature[power=>4]|mybattlefield) then damage:2 auto=damage:X +auto=if type(creature[power>=4]|mybattlefield)~morethan~0 then damage:2 text=Crater's Claws deals X damage to target creature or player. -- Ferocious -- Crater's Claws deals X plus 2 damage to that creature or player instead if you control a creature with power 4 or greater. mana={R}{X} type=Sorcery @@ -27047,8 +27047,7 @@ type=Sorcery [/card] [card] name=Death Grasp -target=creature,player -auto=damage:X +auto=damage:X target(creature,player) auto=life:X controller text=Death Grasp deals X damage to target creature or player. You gain X life. mana={X}{W}{B} @@ -29122,8 +29121,7 @@ subtype=Aura [/card] [card] name=Devil's Play -target=creature,player -auto=damage:X +auto=damage:X target(creature,player) flashback={X}{R}{R}{R} text=Devil's Play deals X damage to target creature or player. -- Flashback {X}{R}{R}{R} (You may cast this card from your graveyard for its flashback cost. Then exile it.) mana={X}{R} @@ -30346,6 +30344,7 @@ target=creature|graveyard auto=moveTo(ownerhand) auto=alternative moveTo(ownerhand) target(creature|graveyard) other={1}{B}{T(creature[black]|mybattlefield)}{T(creature[black]|mybattlefield)} (Pay Conspire) +otherrestriction=type(creature[black]|myBattlefield)~morethan~1 text=Return target creature card in a graveyard to its owner's hand. -- Conspire (As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.) mana={1}{B} type=Sorcery @@ -30539,7 +30538,7 @@ toughness=6 [card] name=Djinn of Wishes auto=counter(0/0,3,wish) -auto={C(0/0,-1,wish)}:reveal:1 optionone name(Reveal card to cast) target(*|reveal) transforms((,newability[castcard(putinplay)])) forever optiononeend optiontwo all(*|reveal) moveto(exile) optiontwoend revealend +auto={2}{U}{U}{C(0/0,-1,wish)}:reveal:1 optionone name(Reveal card to cast) target(*|reveal) transforms((,newability[castcard(putinplay)])) forever optiononeend optiontwo all(*|reveal) moveto(exile) optiontwoend revealend abilities=flying text=Flying -- Djinn of Wishes enters the battlefield with three wish counters on it. -- {2}{U}{U}, Remove a wish counter from Djinn of Wishes: Reveal the top card of your library. You may play that card without paying its mana cost. If you don't, exile it. mana={3}{U}{U} @@ -39064,8 +39063,7 @@ toughness=6 [/card] [card] name=Fanning the Flames -target=creature,player -auto=damage:X +auto=damage:X target(creature,player) buyback={X}{3}{R}{R} text=Buyback {3} (You may pay an additional {3} as you cast this spell. If you do, put this card into your hand as it resolves.) -- Fanning the Flames deals X damage to target creature or player. mana={X}{R}{R} @@ -45688,6 +45686,7 @@ auto=draw:2 auto=reject target(*|myhand) auto=alternative draw:2 reject target(*|myhand) other={2}{U}{T(creature[blue]|mybattlefield)}{T(creature[blue]|mybattlefield)} name(Pay Conspire) +otherrestriction=type(creature[blue]|myBattlefield)~morethan~1 text=Draw two cards, then discard a card. -- Conspire (As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it.) mana={2}{U} type=Sorcery @@ -46405,6 +46404,7 @@ name=Giantbaiting auto=token(Giant,Creature Giant Warrior,4/4,haste,unearth,red green) auto=alternative token(Giant,Creature Giant Warrior,4/4,haste,unearth,red green) other={2}{RG}{T(creature[red;green]|mybattlefield)}{T(creature[red;green]|mybattlefield)} name(Pay Conspire) +otherrestriction=type(creature[green;red]|myBattlefield)~morethan~1 text=Put a 4/4 red and green Giant Warrior creature token with haste onto the battlefield. Exile it at the beginning of the next end step. -- Conspire (As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it.) mana={2}{RG} type=Sorcery @@ -46990,6 +46990,7 @@ target=artifact,enchantment auto=destroy auto=alternative destroy target(artifact,enchantment) other={1}{G}{T(creature[green]|mybattlefield)}{T(creature[green]|mybattlefield)} name(Pay Conspire) +otherrestriction=type(creature[green]|myBattlefield)~morethan~1 text=Destroy target artifact or enchantment. -- Conspire (As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.) mana={1}{G} type=Sorcery @@ -47827,7 +47828,7 @@ toughness=2 [card] name=Goblin Dark-Dwellers abilities=menace -auto=may name(cast card) castcard(normal) target(instant,sorcery[manacost<=3]|mygraveyard) and!(transforms((,newability[exiledeath])) forever)! +auto=may name(cast card) castcard(normal) target(*[instant;sorcery;manacost<=3]|mygraveyard) and!(transforms((,newability[exiledeath])) forever)! text=Menace -- When Goblin Dark-Dwellers enters the battlefield, you may cast target instant or sorcery card with converted mana cost 3 or less from your graveyard without paying its mana cost. If that card would be put into your graveyard this turn, exile it instead. mana={3}{R}{R} type=Creature @@ -51879,11 +51880,11 @@ toughness=5 [/card] [card] name=Gwyllion Hedge-Mage -auto=if type(plains|mybattlefield)~morethan~1 then if type(swamp|mybattlefield)~lessthan~2 then may token(Kithkin Soldier,creature Kithikin Soldier,1/1,white)*2 oneshot +auto=if type(plains|mybattlefield)~morethan~1 then if type(swamp|mybattlefield)~lessthan~2 then may token(Kithkin Soldier,creature Kithikin Soldier,1/1,white) oneshot auto=if type(swamp|mybattlefield)~morethan~1 then if type(plains|mybattlefield)~lessthan~2 then may counter(-1/-1,1) target(creature) oneshot -auto=if type(plains|mybattlefield)~morethan~1 then if type(swamp|mybattlefield)~morethan~1 then choice token(Kithkin Soldier,creature Kithikin Soldier,1/1,white)*2 oneshot +auto=if type(plains|mybattlefield)~morethan~1 then if type(swamp|mybattlefield)~morethan~1 then choice token(Kithkin Soldier,creature Kithikin Soldier,1/1,white) oneshot auto=if type(plains|mybattlefield)~morethan~1 then if type(swamp|mybattlefield)~morethan~1 then choice target(creature) counter(-1/-1,1) oneshot -auto=if type(plains|mybattlefield)~morethan~1 then if type(swamp|mybattlefield)~morethan~1 then name(do both) choice name(do both) target(creature) counter(-1/-1,1) oneshot && name(do both) token(Kithkin Soldier,creature Kithikin Soldier,1/1,white)*2 oneshot +auto=if type(plains|mybattlefield)~morethan~1 then if type(swamp|mybattlefield)~morethan~1 then name(do both) choice name(do both) target(creature) counter(-1/-1,1) oneshot && name(do both) token(Kithkin Soldier,creature Kithikin Soldier,1/1,white) oneshot text=When Gwyllion Hedge-Mage enters the battlefield, if you control two or more Plains, you may put a 1/1 white Kithkin Soldier creature token onto the battlefield. -- When Gwyllion Hedge-Mage enters the battlefield, if you control two or more Swamps, you may put a -1/-1 counter on target creature. mana={2}{WB} type=Creature @@ -67040,7 +67041,7 @@ type=Instant [/card] [card] name=Lifeforce -auto={G}{G}:fizzle target(*[black]|stack) +auto={G}{G}:fizzle target(sorcery|stack) text={G}{G}: Counter target black spell. mana={G}{G} type=Enchantment @@ -72785,6 +72786,7 @@ target=player auto=deplete:4 auto=alternative deplete:4 target(player) other={UB}{T(creature[black;blue]|mybattlefield)}{T(creature[black;blue]|mybattlefield)} name(Pay Conspire) +otherrestriction=type(creature[black;blue]|myBattlefield)~morethan~1 text=Target player puts the top four cards of his or her library into his or her graveyard. -- Conspire (As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.) mana={UB} type=Sorcery @@ -93610,9 +93612,8 @@ subtype=Aura [/card] [card] name=Red Sun's Zenith -target=creature,player -auto=teach(creature) exiledeath -auto=damage:X +auto=choice name(Target creature) exiledeath && damage:X target(creature) +auto=choice name(Target player) damage:X target(player) alias=135262 text=Red Sun's Zenith deals X damage to target creature or player. If a creature dealt damage this way would die this turn, exile it instead. Shuffle Red Sun's Zenith into its owner's library. mana={X}{R} @@ -99522,7 +99523,7 @@ subtype=Sarkhan [/card] [card] name=Sarkhan Unbroken -auto=counter(0/0,9,Loyalty) +auto=counter(0/0,4,Loyalty) aicode=activate moveto(mybattlefield) notatarget(creature[dragon]|mylibrary) auto={C(0/0,1,Loyalty)}:name(+1: Draw card) draw:1 controller && transforms((,newability[activatechooseacolor add{chosencolor} activatechooseend])) forever asSorcery auto={C(0/0,-2,Loyalty)}:name(-2: 4/4 dragon token) token(Dragon,creature dragon,4/4,flying,red) controller asSorcery @@ -99873,7 +99874,7 @@ toughness=4 name=Sawtooth Loon abilities=flying auto=moveTo(ownerhand) notatarget(creature[white;blue]|myBattlefield) -auto=draw:2 && transforms((,newability[target(<2>*|myhand) bottomoflibrary])) +auto=draw:2 && transforms((,newability[target(<2>*|myhand) bottomoflibrary])) oneshot text=Flying -- When Sawtooth Loon enters the battlefield, return a white or blue creature you control to its owner's hand. -- When Sawtooth Loon enters the battlefield, draw two cards, then put two cards from your hand on the bottom of your library. mana={2}{W}{U} type=Creature @@ -119846,7 +119847,8 @@ name=Tezzeret, Agent of Bolas auto=counter(0/0,3,loyalty) aicode=activate target(artifact[zpos<=5]|mylibrary) moveto(myhand) auto={C(0/0,1,Loyalty)}:reveal:5 optionone name(Get artifact) target(artifact|reveal) moveto(myhand) optiononeend optiontwo name(put on bottom) target(<5>*|reveal) bottomoflibrary optiontwoend revealend -auto={C(0/0,-1,Loyalty)}:name(Create Construct) target(artifact) transforms((Construct,Artifact Creature,setpower=5,settoughness=5)) auto={C(0/0,-4,Loyalty)}:target(player) life:-twicetype:artifact:mybattlefield && life:twicetype:artifact:mybattlefield controller +auto={C(0/0,-1,Loyalty)}:name(Create Construct) target(artifact) transforms((Construct Artifact Creature,setpower=5,settoughness=5)) + auto={C(0/0,-4,Loyalty)}:target(player) life:-twicetype:artifact:mybattlefield && life:twicetype:artifact:mybattlefield controller text=+1: Look at the top five cards of your library. You may reveal an artifact card from among them and put it into your hand. Put the rest on the bottom of your library in any order. -- -1: Target artifact becomes a 5/5 artifact creature. -- -4: Target player loses X life and you gain X life, where X is twice the number of artifacts you control. mana={2}{U}{B} type=Legendary Planeswalker @@ -123712,6 +123714,7 @@ auto=tap auto=dynamicability auto=alternative target(creature) dynamicability tap other={4}{BR}{T(creature[black;red]|mybattlefield)}{T(creature[black;red]|mybattlefield)} name(Conspire) +otherrestriction=type(creature[black;red]|myBattlefield)~morethan~1 text=Tap target untapped creature. It deals damage equal to its power to its controller. -- Conspire (As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.) mana={4}{BR} type=Sorcery @@ -130625,8 +130628,7 @@ type=Instant [/card] [card] name=Volcanic Geyser -target=creature,player -auto=damage:X +auto=damage:X target(creature,player) text=Volcanic Geyser deals X damage to target creature or player. mana={X}{R}{R} type=Instant diff --git a/projects/mtg/src/AIPlayerBaka.cpp b/projects/mtg/src/AIPlayerBaka.cpp index 9be36ffac..fd9fb3738 100644 --- a/projects/mtg/src/AIPlayerBaka.cpp +++ b/projects/mtg/src/AIPlayerBaka.cpp @@ -342,14 +342,14 @@ int OrderedAIAction::getEfficiency() // Bonus if almost no cards in hand if (p->game->hand->nb_cards <= 1) { - efficiency = 60; + efficiency = 50; } 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) + if (coreAbilityCardTarget && !coreAbilityCardTarget->isCreature() && !coreAbilityCardTarget->isTapped() && currentPhase == MTG_PHASE_FIRSTMAIN) { - efficiency = 60; + efficiency = 50; } break; } @@ -603,8 +603,8 @@ int OrderedAIAction::getEfficiency() } else { - // 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 + // We don't want to return the ability source cards that are in play to own hand, save rare combos + // cards like Blinking Spirit used to be auto lose for AI if(z == p->game->hand || z == p->game->library) efficiency = 1; else efficiency = 50; @@ -662,8 +662,8 @@ int OrderedAIAction::getEfficiency() SAFE_DELETE(parsedAICC); } - else - efficiency = 0; + else // this is why the AI never chooses any card at all? reveal is used to get cards so it should be at better value + efficiency = 60; } //At this point the "basic" efficiency is computed, we further tweak it depending on general decisions, independent of theAbility type @@ -708,7 +708,14 @@ int OrderedAIAction::getEfficiency() if (ability->source) if(ability->source->hasType(Subtypes::TYPE_PLANESWALKER)) - efficiency += 50; + efficiency += 40; + else if(ability->source->hasType(Subtypes::TYPE_LAND)) + { // probably a shockland, don't pay life if hand is empty + if (p->game->hand->nb_cards == 0 || p->life<=2) + // check that's not a manland(like Celestial Colonnade) + if(efficiency < 50) + efficiency = 0; + } SAFE_DELETE(transAbility); return efficiency; @@ -2365,6 +2372,10 @@ int AIPlayerBaka::chooseTarget(TargetChooser * _tc, Player * forceTarget,MTGCard cardEffect = af.abilityEfficiency(withoutGuessing,this,MODE_TARGET,tc,NULL); delete withoutGuessing; } + // Don't really like it but green mana producing auras targeting the player is one of the most reported bugs + if(cardEffect == BAKA_EFFECT_DONTKNOW && tc->source->hasSubtype(Subtypes::TYPE_AURA) && tc->source->hasColor(Constants::MTG_COLOR_GREEN)) + cardEffect = BAKA_EFFECT_GOOD; + if (cardEffect != BAKA_EFFECT_GOOD) { target = this->opponent(); @@ -2571,164 +2582,164 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty if( card->has(Constants::CANPLAYFROMGRAVEYARD) || card->has(Constants::TEMPFLASHBACK) || hasFlashback ) { - if (!CanHandleCost(card->getManaCost(),card)) - continue; + if (!CanHandleCost(card->getManaCost(),card)) + continue; - if (hasFlashback && !CanHandleCost(card->getManaCost()->getFlashback(),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; + // 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) - continue; - } - else - { - if (game->playRestrictions->canPutIntoZone(card, game->stack) == PlayRestriction::CANT_PLAY) - continue; - } + if (card->hasType(Subtypes::TYPE_LAND)) + { + if (game->playRestrictions->canPutIntoZone(card, game->inPlay) == PlayRestriction::CANT_PLAY) + continue; + } + else + { + if (game->playRestrictions->canPutIntoZone(card, game->stack) == PlayRestriction::CANT_PLAY) + continue; + } - if (card->hasType(Subtypes::TYPE_LEGENDARY) && game->inPlay->findByName(card->name)) - continue; - //glimmervoid alias to avoid ai stalling the game as the hint combo is stuck - //next card to play was galvanic blast but on activate combo it clashes with glimmervoid... - if ((card->alias == 48132) && (card->controller()->game->inPlay->countByType("artifact") < 1)) - continue; + if (card->hasType(Subtypes::TYPE_LEGENDARY) && game->inPlay->findByName(card->name)) + continue; + //glimmervoid alias to avoid ai stalling the game as the hint combo is stuck + //next card to play was galvanic blast but on activate combo it clashes with glimmervoid... + if ((card->alias == 48132) && (card->controller()->game->inPlay->countByType("artifact") < 1)) + continue; - if (card->has(Constants::TREASON) && observer->getCurrentGamePhase() != MTG_PHASE_FIRSTMAIN) - continue; + if (card->has(Constants::TREASON) && observer->getCurrentGamePhase() != MTG_PHASE_FIRSTMAIN) + continue; - if (card->hasType(Subtypes::TYPE_PLANESWALKER) && card->types.size() > 0 && game->inPlay->hasTypeSpecificInt(Subtypes::TYPE_PLANESWALKER,card->types[1])) - continue; + if (card->hasType(Subtypes::TYPE_PLANESWALKER) && card->types.size() > 0 && game->inPlay->hasTypeSpecificInt(Subtypes::TYPE_PLANESWALKER,card->types[1])) + continue; - if(hints && hints->HintSaysItsForCombo(observer,card)) - { - if(hints->canWeCombo(observer,card,this)) - { - AbilityFactory af(observer); - int canPlay = af.parseCastRestrictions(card,card->controller(),card->getRestrictions()); - if(!canPlay) - continue; - nextCardToPlay = card; - gotPayments.clear(); - if((!pMana->canAfford(nextCardToPlay->getManaCost()) || nextCardToPlay->getManaCost()->getKicker())) - gotPayments = canPayMana(nextCardToPlay,nextCardToPlay->getManaCost()); - return activateCombo(); - } - else - { - nextCardToPlay = NULL; - continue; - } - } - int currentCost = card->getManaCost()->getConvertedCost(); - int hasX = card->getManaCost()->hasX(); - gotPayments.clear(); - if((!pMana->canAfford(card->getManaCost()) || card->getManaCost()->getKicker())) - gotPayments = canPayMana(card,card->getManaCost()); - //for preformence reason we only look for specific mana if the payment couldn't be made with pmana. - if ((currentCost > maxCost || hasX) && (gotPayments.size() || pMana->canAfford(card->getManaCost()))) - { - TargetChooserFactory tcf(observer); - TargetChooser * tc = tcf.createTargetChooser(card); - int shouldPlayPercentage = 0; - if (tc) - { - int hasTarget = chooseTarget(tc,NULL,NULL,true); - if( - (tc->maxtargets > hasTarget && tc->maxtargets > 1 && !tc->targetMin && tc->maxtargets != TargetChooser::UNLITMITED_TARGETS) ||//target=<3>creature - (tc->maxtargets == TargetChooser::UNLITMITED_TARGETS && hasTarget < 1)//target=creatures - ) - hasTarget = 0; - if (!hasTarget)//single target covered here. - { - SAFE_DELETE(tc); - continue; - } - shouldPlayPercentage = 90; - if(tc->targetMin && hasTarget < tc->maxtargets) - shouldPlayPercentage = 0; - if(tc->maxtargets > 1 && tc->maxtargets != TargetChooser::UNLITMITED_TARGETS && hasTarget <= tc->maxtargets) - { - int maxA = hasTarget-tc->maxtargets; - shouldPlayPercentage += (10*maxA);//reduce the chances of playing multitarget if we are not above max targets. - } - if(tc->maxtargets == TargetChooser::UNLITMITED_TARGETS) - { - shouldPlayPercentage = 40 + (10*hasTarget); - int totalCost = pMana->getConvertedCost()-currentCost; - int totalTargets = hasTarget+hasTarget; - if(hasX && totalCost <= totalTargets)// {x} spell with unlimited targeting tend to divide damage, we want atleast 1 damage per target before casting. - { - shouldPlayPercentage = 0; - } - } - SAFE_DELETE(tc); - } - else - { - // 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 { - int shouldPlay = effectBadOrGood(card); - if (shouldPlay == BAKA_EFFECT_GOOD) { + if(hints && hints->HintSaysItsForCombo(observer,card)) + { + if(hints->canWeCombo(observer,card,this)) + { + AbilityFactory af(observer); + int canPlay = af.parseCastRestrictions(card,card->controller(),card->getRestrictions()); + if(!canPlay) + continue; + nextCardToPlay = card; + gotPayments.clear(); + if((!pMana->canAfford(nextCardToPlay->getManaCost()) || nextCardToPlay->getManaCost()->getKicker())) + gotPayments = canPayMana(nextCardToPlay,nextCardToPlay->getManaCost()); + return activateCombo(); + } + else + { + nextCardToPlay = NULL; + continue; + } + } + int currentCost = card->getManaCost()->getConvertedCost(); + int hasX = card->getManaCost()->hasX(); + gotPayments.clear(); + if((!pMana->canAfford(card->getManaCost()) || card->getManaCost()->getKicker())) + gotPayments = canPayMana(card,card->getManaCost()); + //for preformence reason we only look for specific mana if the payment couldn't be made with pmana. + if ((currentCost > maxCost || hasX) && (gotPayments.size() || pMana->canAfford(card->getManaCost()))) + { + TargetChooserFactory tcf(observer); + TargetChooser * tc = tcf.createTargetChooser(card); + int shouldPlayPercentage = 0; + if (tc) + { + int hasTarget = chooseTarget(tc,NULL,NULL,true); + if( + (tc->maxtargets > hasTarget && tc->maxtargets > 1 && !tc->targetMin && tc->maxtargets != TargetChooser::UNLITMITED_TARGETS) ||//target=<3>creature + (tc->maxtargets == TargetChooser::UNLITMITED_TARGETS && hasTarget < 1)//target=creatures + ) + hasTarget = 0; + if (!hasTarget)//single target covered here. + { + SAFE_DELETE(tc); + continue; + } + shouldPlayPercentage = 90; + if(tc->targetMin && hasTarget < tc->maxtargets) + shouldPlayPercentage = 0; + if(tc->maxtargets > 1 && tc->maxtargets != TargetChooser::UNLITMITED_TARGETS && hasTarget <= tc->maxtargets) + { + int maxA = hasTarget-tc->maxtargets; + shouldPlayPercentage += (10*maxA);//reduce the chances of playing multitarget if we are not above max targets. + } + if(tc->maxtargets == TargetChooser::UNLITMITED_TARGETS) + { + shouldPlayPercentage = 40 + (10*hasTarget); + int totalCost = pMana->getConvertedCost()-currentCost; + int totalTargets = hasTarget+hasTarget; + if(hasX && totalCost <= totalTargets)// {x} spell with unlimited targeting tend to divide damage, we want atleast 1 damage per target before casting. + { + shouldPlayPercentage = 0; + } + } + SAFE_DELETE(tc); + } + else + { + // 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 { - // shouldPlay == baka_effect_bad giving it a 10 for odd ball lottery chance. - shouldPlayPercentage = 10; + 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) - { - int xDiff = pMana->getConvertedCost() - currentCost; - if (xDiff < 0) - xDiff = 0; - shouldPlayPercentage = shouldPlayPercentage - static_cast ((shouldPlayPercentage * 1.9f) / (1 + xDiff)); - } - if(card->getManaCost() && card->getManaCost()->getKicker() && card->getManaCost()->getKicker()->isMulti) - { - shouldPlayPercentage = 10* size_t(gotPayments.size())/int(1+(card->getManaCost()->getConvertedCost()+card->getManaCost()->getKicker()->getConvertedCost())); - if(shouldPlayPercentage <= 10) - shouldPlayPercentage = shouldPlayPercentage/3; - } - DebugTrace("Should I play " << (card ? card->name : "Nothing" ) << "?" << endl - <<"shouldPlayPercentage = "<< shouldPlayPercentage); - if(card->getRestrictions().size()) - { - AbilityFactory af(observer); - int canPlay = af.parseCastRestrictions(card,card->controller(),card->getRestrictions()); - if(!canPlay) - continue; - } - int randomChance = randomGenerator.random(); - int chance = randomChance % 100; - if (chance > shouldPlayPercentage) - continue; - if(shouldPlayPercentage <= 10) - { - DebugTrace("shouldPlayPercentage was less than 10 this was a lottery roll on RNG"); - } - nextCardToPlay = card; - maxCost = currentCost; - if (hasX) - maxCost = pMana->getConvertedCost(); - } + //Reduce the chances of playing a spell with X cost if available mana is low + if (hasX) + { + int xDiff = pMana->getConvertedCost() - currentCost; + if (xDiff < 0) + xDiff = 0; + shouldPlayPercentage = shouldPlayPercentage - static_cast ((shouldPlayPercentage * 1.9f) / (1 + xDiff)); + } + if(card->getManaCost() && card->getManaCost()->getKicker() && card->getManaCost()->getKicker()->isMulti) + { + shouldPlayPercentage = 10* size_t(gotPayments.size())/int(1+(card->getManaCost()->getConvertedCost()+card->getManaCost()->getKicker()->getConvertedCost())); + if(shouldPlayPercentage <= 10) + shouldPlayPercentage = shouldPlayPercentage/3; + } + DebugTrace("Should I play from grave " << (card ? card->name : "Nothing" ) << "?" << endl + <<"shouldPlayPercentage = "<< shouldPlayPercentage); + if(card->getRestrictions().size()) + { + AbilityFactory af(observer); + int canPlay = af.parseCastRestrictions(card,card->controller(),card->getRestrictions()); + if(!canPlay) + continue; + } + int randomChance = randomGenerator.random(); + int chance = randomChance % 100; + if (chance > shouldPlayPercentage) + continue; + if(shouldPlayPercentage <= 10) + { + DebugTrace("shouldPlayPercentage was less than 10 this was a lottery roll on RNG"); + } + nextCardToPlay = card; + maxCost = currentCost; + if (hasX) + maxCost = pMana->getConvertedCost(); + } } } //canplayfromexile @@ -2863,7 +2874,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty if(shouldPlayPercentage <= 10) shouldPlayPercentage = shouldPlayPercentage/3; } - DebugTrace("Should I play " << (card ? card->name : "Nothing" ) << "?" << endl + DebugTrace("Should I play from exile" << (card ? card->name : "Nothing" ) << "?" << endl <<"shouldPlayPercentage = "<< shouldPlayPercentage); if(card->getRestrictions().size()) { @@ -2912,8 +2923,9 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty if (card->has(Constants::TREASON) && observer->getCurrentGamePhase() != MTG_PHASE_FIRSTMAIN) continue; - if (card->hasType(Subtypes::TYPE_PLANESWALKER) && card->types.size() > 0 && game->inPlay->hasTypeSpecificInt(Subtypes::TYPE_PLANESWALKER,card->types[1])) - continue; + //PLaneswalkers are now legendary so this is redundant + //if (card->hasType(Subtypes::TYPE_PLANESWALKER) && card->types.size() > 0 && game->inPlay->hasTypeSpecificInt(Subtypes::TYPE_PLANESWALKER,card->types[1])) + //continue; if(hints && hints->HintSaysItsForCombo(observer,card)) { @@ -3017,7 +3029,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty if(shouldPlayPercentage <= 10) shouldPlayPercentage = shouldPlayPercentage/3; } - DebugTrace("Should I play " << (card ? card->name : "Nothing" ) << "?" << endl + DebugTrace("Should I play from hand" << (card ? card->name : "Nothing" ) << "?" << endl <<"shouldPlayPercentage = "<< shouldPlayPercentage); if(card->getRestrictions().size()) { @@ -3444,12 +3456,17 @@ int AIPlayerBaka::getCreaturesInfo(Player * player, int neededInfo, int untapMod int AIPlayerBaka::chooseAttackers() { + int myCreatures = getCreaturesInfo(this, INFO_NBCREATURES, -1, 1); + if (myCreatures < 1) + return 0; //Attack with all creatures //How much damage can the other player do during his next Attack ? int opponentForce = getCreaturesInfo(opponent(), INFO_CREATURESPOWER); int opponentCreatures = getCreaturesInfo(opponent(), INFO_NBCREATURES); int myForce = getCreaturesInfo(this, INFO_CREATURESPOWER, -1, 1); - int myCreatures = getCreaturesInfo(this, INFO_NBCREATURES, -1, 1); + if(opponent()->life < 5) + agressivity += 31; + bool attack = ((myCreatures > opponentCreatures) || (myForce > opponentForce) || (myForce > 2 * opponent()->life)); if (agressivity > 80 && !attack && life > opponentForce) { @@ -3466,7 +3483,7 @@ int AIPlayerBaka::chooseAttackers() MTGCardInstance * card = NULL; while ((card = cd.nextmatch(game->inPlay, card))) { - if (hints && hints->HintSaysAlwaysAttack(observer, card)) + if ((hints && hints->HintSaysAlwaysAttack(observer, card)) || card->has(Constants::UNBLOCKABLE)) { if (!card->isAttacker()) { diff --git a/projects/mtg/src/ActionStack.cpp b/projects/mtg/src/ActionStack.cpp index 0fe247b47..e98e1f5de 100644 --- a/projects/mtg/src/ActionStack.cpp +++ b/projects/mtg/src/ActionStack.cpp @@ -274,8 +274,8 @@ void StackAbility::Render() else fmLibrary++; } - else - mytargetQuads.push_back( ((Damageable *)(tt))->getIcon() ); + //else // This was crashing the game when a permanent targeted a spell in the stack + //mytargetQuads.push_back( ((Damageable *)(tt))->getIcon() ); } } } diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 7c0ae786e..9d379e7f9 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -4626,6 +4626,10 @@ int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, Targ return BAKA_EFFECT_GOOD; if (dynamic_cast (a)) return BAKA_EFFECT_GOOD; + if (dynamic_cast (a)) + return BAKA_EFFECT_GOOD; + if (dynamic_cast (a)) + return BAKA_EFFECT_BAD; if (PTInstant * abi = dynamic_cast(a)) return (abi->wppt->power.getValue() >= 0 && abi->wppt->toughness.getValue() >= 0) ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD; diff --git a/projects/mtg/src/MTGCardInstance.cpp b/projects/mtg/src/MTGCardInstance.cpp index 27edf2909..10e2370bd 100644 --- a/projects/mtg/src/MTGCardInstance.cpp +++ b/projects/mtg/src/MTGCardInstance.cpp @@ -1353,16 +1353,18 @@ int MTGCardInstance::DangerRanking() result += 1; } } - if (result > 1) - danger += 1; - if (result > 2) - danger += 1; + // Even at 60(danger=3) the AI is hasty to play removal on a simple creature + // a vanilla 2 mana, 2/2 used to be eff = 60 if (result > 4) danger += 1; if (result > 6) danger += 1; if (result > 10) danger += 1; + if (result > 12) + danger += 1; + if (result > 14) + danger += 1; return danger; }