Added/fixed primitives, Improved AI: now it can plays cards using morph cost too.

This commit is contained in:
Vittorio Alfieri
2021-08-25 15:30:44 +02:00
parent c7c2025fc9
commit bbc25e2727
5 changed files with 149 additions and 81 deletions

View File

@@ -47910,6 +47910,16 @@ power=3
toughness=3
[/card]
[card]
name=Psychic Transfer
other={4}{U} name(Psychic Transfer)
otherrestriction=compare(lifetotalminusopponentlifetotalminusend)~morethan~4
restriction=compare(opponentlifetotalminuslifetotalminusend)~morethan~4
auto=exchangelife opponent
text=If the difference between your life total and target player's life total is 5 or less, exchange life totals with that player.
mana={4}{U}
type=Sorcery
[/card]
[card]
name=Psychotic Episode
abilities=madness
auto=if type(*|opponenthand)~morethan~0 then choice name(Look opponent's hand) target(*|opponenthand) bottomoflibrary
@@ -47948,6 +47958,14 @@ power=3
toughness=3
[/card]
[card]
name=Pull from Eternity
target=*|exile
auto=moveto(ownergraveyard)
text=Put target face-up exiled card into its owner's graveyard.
mana={W}
type=Instant
[/card]
[card]
name=Pull from Tomorrow
auto=draw:X && reject notatarget(*|myhand)
text=Draw X cards, then discard a card.
@@ -47990,6 +48008,14 @@ mana={1}{R}{G}
type=Sorcery
[/card]
[card]
name=Purgatory
auto=@movedto(creature[-token]|mygraveyard) from(battlefield):name(Exile creature) all(trigger[to]) moveto(myexile) and!( counter(0/0.1.PurgatoryExile) )!
auto=@each my upkeep restriction{type(creature|myexile)~morethan~0}:may pay({4}) name(Return exiled creature) target(creature[counter{0/0.1.PurgatoryExile}]|myexile) moveto(myBattlefield) and!( life:-2 controller )!
text=Whenever a nontoken creature is put into your graveyard from the battlefield, exile that card. -- At the beginning of your upkeep, you may pay {4} and 2 life. If you do, return a card exiled with Purgatory to the battlefield.
mana={2}{W}{B}
type=Enchantment
[/card]
[card]
name=Purphoros's Intervention
auto=choice name(Create elemental token) token(Elemental,Creature Elemental,X/1,red,trample,haste,unearth)
auto=choice name(Deals twice X damage) damage:X target(creature,planeswalker) && damage:X target(creature,planeswalker)
@@ -48136,6 +48162,41 @@ mana={4}{R}{R}
type=Instant
[/card]
[card]
name=Pyromancer's Gauntlet
auto=@damaged(*|opponentBattlefield) from(instant[red]|myzones):all(trigger[to]) transforms((,newability[damage:2])) oneshot
auto=@damaged(*|opponentBattlefield) from(sorcery[red]|myzones):all(trigger[to]) transforms((,newability[damage:2])) oneshot
auto=@damaged(*|opponentBattlefield) from(planeswalker[red]|myzones):all(trigger[to]) transforms((,newability[damage:2])) oneshot
auto=@damagefoeof(player) from(instant[red]|myzones):name(Damage opponent) damage:2 opponent
auto=@damagefoeof(player) from(sorcery[red]|myzones):name(Damage opponent) damage:2 opponent
auto=@damagefoeof(player) from(planeswalker[red]|myzones):name(Damage opponent) damage:2 opponent
text=If a red instant or sorcery spell you control or a red planeswalker you control would deal damage to a permanent or player, it deals that much damage plus 2 to that permanent or player instead.
mana={5}
type=Artifact
[/card]
[card]
name=Pyromancer's Goggles
auto={T}:name(Add red mana) transforms((,newability[add{R}],newability[@movedto(*[sorcery;instant;red]|mystack) turnlimited:all(trigger[to]) activate castcard(copied noevent)])) ueot
text={T}: Add {R} to your mana pool. When that mana is spent to cast a red instant or sorcery spell, copy that spell and you may choose new targets for the copy.
mana={5}
type=Legendary Artifact
[/card]
[card]
name=Pyromancer's Swath
auto=@damaged(*|opponentBattlefield) from(*[instant;sorcery]|myzones):all(trigger[to]) transforms((,newability[damage:2])) oneshot
auto=@damagefoeof(player) from(*[instant;sorcery]|myzones):name(Damage opponent) damage:2 opponent
auto=@each endofturn:name(Discard hand) all(*|myhand) reject
text=If an instant or sorcery source you control would deal damage to a creature or player, it deals that much damage plus 2 to that creature or player instead. -- At the beginning of each end step, discard your hand.
mana={2}{R}
type=Enchantment
[/card]
[card]
name=Pyromancy
auto={D}{3}:name(Deal damage) transforms((,newability[all(*[zpos=type:*:mygraveyard]|mygraveyard) transforms((,newability[name(Damage creature or player) damage:manacost target(player^creature)])) oneshot])) oneshot
text={3}, Discard a card at random: Pyromancy deals damage to target creature or player equal to the converted mana cost of the discarded card.
mana={2}{R}{R}
type=Enchantment
[/card]
[card]
name=Pyromantic Pilgrim
abilities=haste
text=Haste (This creature can attack and {T} as soon as it comes under your control.)
@@ -48187,6 +48248,14 @@ mana={4}{R}
type=Sorcery
[/card]
[card]
name=Pyxis of Pandemonium
auto={T}:name(Exile top cards) all(*[zpos=1]|library) moveto(ownerexile) and!( counter(0/0.1.PyxisExiled) )!
auto={7}{T}:name(Put permanents in play) all(*[-instant;-sorcery;counter{0/0.1.PyxisExiled}]|exile) moveto(ownerbattlefield)
text={T}: Each player exiles the top card of his or her library face down. -- {7}, {T}, Sacrifice Pyxis of Pandemonium: Each player turns face up all cards he or she owns exiled with Pyxis of Pandemonium, then puts all permanent cards among them onto the battlefield.
mana={1}
type=Artifact
[/card]
[card]
name=Qarsi Deceiver
auto={T}:add{C}
text={T}: Add {1} to your mana pool. Spend this mana only to cast a face-down creature spell, pay a mana cost to turn a manifested creature face up, or pay a morph cost. (A megamorph cost is a morph cost.)
@@ -48445,6 +48514,17 @@ mana={3}{R}
type=Sorcery
[/card]
[card]
name=Quest for Pure Flame
auto=@damagefoeof(player) from(*[instant;sorcery]|stack,Graveyard):name(Put Quest counter) counter(0/0.1.quest)
auto=@damagefoeof(player) from(*[enchantment;artifact;land;planeswalker]|battlefield,Graveyard):name(Put Quest counter) counter(0/0.1.quest)
auto=@damagefoeof(player) from(creature[-Fiendish Duo]|battlefield,Graveyard):name(Put Quest counter) counter(0/0.1.quest)
auto=@combatdamagefoeof(player) from(this):damage:name(Put Quest counter) counter(0/0.1.quest)
auto={S} restriction{compare(hascntquest)~morethan~3}:name(Sacrifice and double damage) emblem transforms((,newability[@damagefoeof(player) from(*[instant;sorcery]|myzones):damage:thatmuch all(trigger[to])],newability[@damagefoeof(player) from(*[creature;artifact;land;planeswalker]|myzones):damage:thatmuch all(trigger[to])],newability[@damagefoeof(player) from(enchantment[-Quest for Pure Flame]|myzones):damage:thatmuch all(trigger[to])])) ueot dontremove
text=Whenever a source you control deals damage to an opponent, you may put a quest counter on Quest for Pure Flame. -- Remove four quest counters from Quest for Pure Flame and sacrifice it: If any source you control would deal damage to a creature or player this turn, it deals double that damage to that creature or player instead.
mana={R}
type=Enchantment
[/card]
[card]
name=Questing Beast
abilities=vigilance,deathtouch,haste
auto=cantbeblockedby(creature[power<=2])
@@ -48627,6 +48707,17 @@ power=4
toughness=2
[/card]
[card]
name=Rag Man
auto=this(variable{ishuman}>0) {B}{B}{B}{T}:name(Look opponent hand) name(Look opponent hand) target(*|opponenthand) transforms((,newability[name(Discard creature) target(creature|myhand) reject])) oneshot myturnonly
auto=this(variable{ishuman}<1) {B}{B}{B}{T}:name(Look opponent hand) name(Look opponent hand) target(*|opponenthand) transforms((,newability[ability$!name(Discard creature) name(Discard creature) target(creature|opponenthand) reject!$ opponent])) oneshot myturnonly
text={B}{B}{B}, {T}: Target opponent reveals his or her hand and discards a creature card at random. Activate this ability only during your turn.
mana={2}{B}{B}
type=Creature
subtype=Human Minion
power=2
toughness=1
[/card]
[card]
name=Ragavan, Nimble Pilferer
other={R}{2} name(Dash)
auto=if paid(alternative) then transforms((,newability[haste],newability[phaseaction[endofturn sourceinplay] moveto(ownerhand) all(this)])) forever

View File

@@ -8663,12 +8663,6 @@ mana={1}{U}
type=Sorcery
[/card]
[card]
name=Psychic Transfer
text=If the difference between your life total and target player's life total is 5 or less, exchange life totals with that player.
mana={4}{U}
type=Sorcery
[/card]
[card]
name=Psychogenic Probe
text=Whenever a spell or ability causes a player to shuffle his or her library, Psychogenic Probe deals 2 damage to him or her.
mana={2}
@@ -8681,12 +8675,6 @@ mana={3}{U}
type=Enchantment
[/card]
[card]
name=Pull from Eternity
text=Put target face-up exiled card into its owner's graveyard.
mana={W}
type=Instant
[/card]
[card]
name=Pulling Teeth
text=Clash with an opponent. If you win, target player discards two cards. Otherwise, that player discards a card. (Each clashing player reveals the top card of his or her library, then puts that card on the top or bottom. A player wins if his or her card had a higher converted mana cost.)
mana={1}{B}
@@ -8713,14 +8701,9 @@ type=Instant
subtype=Arcane
[/card]
[card]
name=Purgatory
text=Whenever a nontoken creature is put into your graveyard from the battlefield, exile that card. -- At the beginning of your upkeep, you may pay {4} and 2 life. If you do, return a card exiled with Purgatory to the battlefield.
mana={2}{W}{B}
type=Enchantment
[/card]
[card]
name=Purity
abilities=flying
auto=@movedto(this|mygraveyard):name(Shuffle in library) all(trigger[to]) moveto(ownerlibrary) and!( shuffle )!
text=Flying -- If noncombat damage would be dealt to you, prevent that damage. You gain life equal to the damage prevented this way. -- When Purity is put into a graveyard from anywhere, shuffle it into its owner's library.
mana={3}{W}{W}{W}
type=Creature
@@ -8768,42 +8751,6 @@ mana={1}{R}
type=Enchantment
[/card]
[card]
name=Pyromancer's Gauntlet
text=If a red instant or sorcery spell you control or a red planeswalker you control would deal damage to a permanent or player, it deals that much damage plus 2 to that permanent or player instead.
mana={5}
type=Artifact
[/card]
[card]
name=Pyromancer's Goggles
text={T}: Add {R} to your mana pool. When that mana is spent to cast a red instant or sorcery spell, copy that spell and you may choose new targets for the copy.
mana={5}
type=Legendary Artifact
[/card]
[card]
name=Pyromancer's Swath
text=If an instant or sorcery source you control would deal damage to a creature or player, it deals that much damage plus 2 to that creature or player instead. -- At the beginning of each end step, discard your hand.
mana={2}{R}
type=Enchantment
[/card]
[card]
name=Pyromancy
text={3}, Discard a card at random: Pyromancy deals damage to target creature or player equal to the converted mana cost of the discarded card.
mana={2}{R}{R}
type=Enchantment
[/card]
[card]
name=Pyxis of Pandemonium
text={T}: Each player exiles the top card of his or her library face down. -- {7}, {T}, Sacrifice Pyxis of Pandemonium: Each player turns face up all cards he or she owns exiled with Pyxis of Pandemonium, then puts all permanent cards among them onto the battlefield.
mana={1}
type=Artifact
[/card]
[card]
name=Quest for Pure Flame
text=Whenever a source you control deals damage to an opponent, you may put a quest counter on Quest for Pure Flame. -- Remove four quest counters from Quest for Pure Flame and sacrifice it: If any source you control would deal damage to a creature or player this turn, it deals double that damage to that creature or player instead.
mana={R}
type=Enchantment
[/card]
[card]
name=Question Elemental?
abilities=flying
text=Flying -- Are you aware that when you say something that isn't a question, the player who first points out this fact gains control of Question Elemental?
@@ -8880,15 +8827,6 @@ mana={3}{R}{R}
type=Instant
[/card]
[card]
name=Rag Man
text={B}{B}{B}, {T}: Target opponent reveals his or her hand and discards a creature card at random. Activate this ability only during your turn.
mana={2}{B}{B}
type=Creature
subtype=Human Minion
power=2
toughness=1
[/card]
[card]
name=Rage Extractor
text=({PR} can be paid with either {R} or 2 life.) -- Whenever you cast a spell with Phyrexian in its mana cost, Rage Extractor deals damage equal to that spell's converted mana cost to target creature or player.
mana={4}{PR}

View File

@@ -93,7 +93,7 @@ class AIPlayerBaka: public AIPlayer{
virtual AIStats * getStats();
bool payAlternative;
int payAlternative;
MTGCardInstance * nextCardToPlay;
MTGCardInstance * activateCombo();
TargetChooser * GetComboTc(GameObserver * observer, TargetChooser * tc = NULL);
@@ -122,6 +122,12 @@ class AIPlayerBaka: public AIPlayer{
INFO_CREATURESATTACKINGPOWER
};
enum {
NONE,
OTHER,
MORPH
}; // Possbile alternative costs to be used for AI.
vector<MTGAbility*>gotPayments;
AIPlayerBaka(GameObserver *observer, string deckFile, string deckfileSmall, string avatarFile, MTGDeck * deck = NULL);

View File

@@ -2750,7 +2750,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
cd.init();
cd.setType(type);
card = NULL;
payAlternative = false;
payAlternative = NONE;
gotPayments = vector<MTGAbility*>();
//canplayfromgraveyard
while ((card = cd.nextmatch(game->graveyard, card)))
@@ -3237,7 +3237,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
card = NULL; // fixed bug causing AI never play a card there are one or more cards in exile or other zones...
while ((card = cd.nextmatch(game->hand, card)))
{
bool localpayAlternative = false;
int localpayAlternative = NONE;
if (!CanHandleCost(card->getManaCost(),card))
continue;
@@ -3293,11 +3293,11 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
ManaCost* manaToPay = card->getManaCost();
if((!pMana->canAfford(card->getManaCost(),0) || card->getManaCost()->getKicker()))
gotPayments = canPayMana(card,card->getManaCost(),card->has(Constants::ANYTYPEOFMANA));
bool hasConvoke = false; //Fix a crash when AI try to pay convoke cost.
bool hasOffering = card->basicAbilities[Constants::OFFERING]; //Fix a hang when AI try to pay emerge cost.
bool hasDelve = false; //Fix a hang when AI try to pay delve cost.
if(card->getManaCost()->getAlternative() && !gotPayments.size() && !pMana->canAfford(card->getManaCost(),0) && !card->getManaCost()->getKicker()){ //Now AI can cast cards using alternative cost.
ManaCost * extra = card->getManaCost()->getAlternative();
bool hasConvoke = false; //Fix a crash when AI try to pay convoke cost.
bool hasOffering = card->basicAbilities[Constants::OFFERING]; //Fix a hang when AI try to pay emerge cost.
bool hasDelve = false; //Fix a hang when AI try to pay delve cost.
if(extra->extraCosts){
for(unsigned int i = 0; i < extra->extraCosts->costs.size() && !hasConvoke && !hasOffering && !hasDelve; i++){
if(dynamic_cast<Convoke*> (extra->extraCosts->costs[i]))
@@ -3309,12 +3309,18 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
}
}
if(!hasOffering && !hasConvoke && !hasDelve){
localpayAlternative = true;
localpayAlternative = OTHER;
manaToPay = card->getManaCost()->getAlternative();
if(!pMana->canAfford(manaToPay,0))
gotPayments = canPayMana(card,card->getManaCost()->getAlternative(),card->has(Constants::ANYTYPEOFMANA));
}
}
}
if(card->getManaCost()->getMorph() && !gotPayments.size() && !pMana->canAfford(card->getManaCost(),0) && !card->getManaCost()->getKicker() && !card->getManaCost()->getAlternative()){ //Now AI can cast cards using morph cost.
localpayAlternative = MORPH;
manaToPay = card->getManaCost()->getMorph();
if(!pMana->canAfford(manaToPay,0))
gotPayments = canPayMana(card,card->getManaCost()->getMorph(),card->has(Constants::ANYTYPEOFMANA));
}
//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(manaToPay,card->has(Constants::ANYTYPEOFMANA))))
{
@@ -3394,13 +3400,13 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
}
DebugTrace("Should I play from hand" << (card ? card->name : "Nothing" ) << "?" << endl
<<"shouldPlayPercentage = "<< shouldPlayPercentage);
if(localpayAlternative){
if(localpayAlternative != NONE){
if(card->getOtherRestrictions().size())
{
AbilityFactory af(observer);
int canPlay = af.parseCastRestrictions(card,card->controller(),card->getOtherRestrictions());
if(!canPlay){
localpayAlternative = false;
localpayAlternative = NONE;
canPlay = true;
if(card->getRestrictions().size())
canPlay = af.parseCastRestrictions(card,card->controller(),card->getRestrictions()); //Check if card can be casted at least with normal cost.
@@ -3413,11 +3419,18 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
{
AbilityFactory af(observer);
int canPlay = af.parseCastRestrictions(card,card->controller(),card->getRestrictions());
if(!canPlay && card->getManaCost()->getAlternative()){
if(!canPlay && (card->getManaCost()->getAlternative() || card->getManaCost()->getMorph())){
canPlay = true;
if(card->getOtherRestrictions().size())
canPlay = af.parseCastRestrictions(card,card->controller(),card->getOtherRestrictions()); //Check if card can be casted at least with other cost.
if(canPlay) localpayAlternative = true;
canPlay = af.parseCastRestrictions(card,card->controller(),card->getOtherRestrictions()); //Check if card can be casted at least with alternative costs (e.g. other or morph).
if(canPlay) {
if(card->getManaCost()->getAlternative() && !hasOffering && !hasConvoke && !hasDelve)
localpayAlternative = OTHER;
else if(card->getManaCost()->getMorph())
localpayAlternative = MORPH;
else
canPlay = false;
}
}
if(!canPlay)
continue;
@@ -3448,9 +3461,12 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
if(!pMana->canAfford(nextCardToPlay->getManaCost()->getRetrace(),0))
gotPayments = canPayMana(nextCardToPlay,nextCardToPlay->getManaCost()->getRetrace(),nextCardToPlay->has(Constants::ANYTYPEOFMANA));
}
} else if(payAlternative){
} else if(payAlternative == OTHER){
if(!pMana->canAfford(nextCardToPlay->getManaCost()->getAlternative(),0)) // Now AI can cast cards using alternative cost.
gotPayments = canPayMana(nextCardToPlay,nextCardToPlay->getManaCost()->getAlternative(),nextCardToPlay->has(Constants::ANYTYPEOFMANA));
} else if(payAlternative == MORPH){
if(!pMana->canAfford(nextCardToPlay->getManaCost()->getMorph(),0)) // Now AI can cast cards using morph cost.
gotPayments = canPayMana(nextCardToPlay,nextCardToPlay->getManaCost()->getMorph(),nextCardToPlay->has(Constants::ANYTYPEOFMANA));
} else {
if(!pMana->canAfford(nextCardToPlay->getManaCost(),0) || nextCardToPlay->getManaCost()->getKicker())
gotPayments = canPayMana(nextCardToPlay,nextCardToPlay->getManaCost(),nextCardToPlay->has(Constants::ANYTYPEOFMANA));
@@ -3655,13 +3671,20 @@ int AIPlayerBaka::computeActions()
gotPayments.clear();
}
}
} else if(payAlternative){ // Now AI can cast cards using alternative cost.
} else if(payAlternative == OTHER){ // Now AI can cast cards using other cost.
if(payTheManaCost(nextCardToPlay->getManaCost()->getAlternative(),nextCardToPlay->has(Constants::ANYTYPEOFMANA),nextCardToPlay,gotPayments))
{
AIAction * a = NEW AIAction(this, nextCardToPlay);
clickstream.push(a);
gotPayments.clear();
}
} else if(payAlternative == MORPH){ // Now AI can cast cards using morph cost.
if(payTheManaCost(nextCardToPlay->getManaCost()->getMorph(),nextCardToPlay->has(Constants::ANYTYPEOFMANA),nextCardToPlay,gotPayments))
{
AIAction * a = NEW AIAction(this, nextCardToPlay);
clickstream.push(a);
gotPayments.clear();
}
} else {
if(payTheManaCost(nextCardToPlay->getManaCost(),nextCardToPlay->has(Constants::ANYTYPEOFMANA),nextCardToPlay,gotPayments))
{
@@ -3839,13 +3862,20 @@ int AIPlayerBaka::computeActions()
gotPayments.clear();
}
}
} else if(payAlternative){ // Now AI can cast cards using alternative cost.
} else if(payAlternative == OTHER){ // Now AI can cast cards using other cost.
if(payTheManaCost(nextCardToPlay->getManaCost()->getAlternative(),nextCardToPlay->has(Constants::ANYTYPEOFMANA),nextCardToPlay,gotPayments))
{
AIAction * a = NEW AIAction(this, nextCardToPlay);
clickstream.push(a);
gotPayments.clear();
}
} else if(payAlternative == MORPH){ // Now AI can cast cards using morph cost.
if(payTheManaCost(nextCardToPlay->getManaCost()->getMorph(),nextCardToPlay->has(Constants::ANYTYPEOFMANA),nextCardToPlay,gotPayments))
{
AIAction * a = NEW AIAction(this, nextCardToPlay);
clickstream.push(a);
gotPayments.clear();
}
} else {
if(payTheManaCost(nextCardToPlay->getManaCost(),nextCardToPlay->has(Constants::ANYTYPEOFMANA),nextCardToPlay,gotPayments))
{