From bf6439db00710ab8c1960a9d05c05b7b44c293ea Mon Sep 17 00:00:00 2001 From: valfieri Date: Sat, 3 Oct 2020 16:54:17 +0200 Subject: [PATCH] Improved Kicker cards, now it's possible to target a specific card with kicker cost and handle any event connected to a kicker casting cost. --- .../bin/Res/sets/primitives/borderline.txt | 6 ++-- projects/mtg/bin/Res/sets/primitives/mtg.txt | 25 +++++++++++------ projects/mtg/include/CardDescriptor.h | 3 ++ projects/mtg/include/MTGDefinitions.h | 3 +- projects/mtg/src/CardDescriptor.cpp | 20 +++++++++++++ projects/mtg/src/CardGui.cpp | 28 +++++++++++++++++-- projects/mtg/src/MTGDefinitions.cpp | 3 +- projects/mtg/src/MTGRules.cpp | 12 ++++++-- projects/mtg/src/TargetChooser.cpp | 28 +++++++++++++++++-- 9 files changed, 109 insertions(+), 19 deletions(-) diff --git a/projects/mtg/bin/Res/sets/primitives/borderline.txt b/projects/mtg/bin/Res/sets/primitives/borderline.txt index f23c1397e..f24aaba6e 100644 --- a/projects/mtg/bin/Res/sets/primitives/borderline.txt +++ b/projects/mtg/bin/Res/sets/primitives/borderline.txt @@ -29482,9 +29482,9 @@ type=Instant [/card] [card] name=Prohibit -other={3}{U} name(Kicker) -auto=ifnot paid(alternative) then target(*[manacost<=2]|stack) fizzle -auto=if paid(alternative) then target(*[manacost<=4]|stack) fizzle +kicker={2} +auto=ifnot paid(kicker) then target(*[manacost<=2]|stack) fizzle +auto=if paid(kicker) then target(*[manacost<=4]|stack) fizzle text=Kicker {2} (You may pay an additional {2} as you cast this spell.) -- Counter target spell if its converted mana cost is 2 or less. If Prohibit was kicked, counter that spell if its converted mana cost is 4 or less instead. mana={1}{U} type=Instant diff --git a/projects/mtg/bin/Res/sets/primitives/mtg.txt b/projects/mtg/bin/Res/sets/primitives/mtg.txt index cabd9e7cc..6111169c1 100644 --- a/projects/mtg/bin/Res/sets/primitives/mtg.txt +++ b/projects/mtg/bin/Res/sets/primitives/mtg.txt @@ -5446,6 +5446,7 @@ toughness=1 [/card] [card] name=Arctic Merfolk +abilities=hasotherkicker auto=alternative counter(1/1,1) all(this) text=Kicker - Return a creature you control to its owner's hand. (You may return a creature you control to its owner's hand in addition to any other costs as you cast this spell.) -- If Arctic Merfolk was kicked, it enters the battlefield with a +1/+1 counter on it. mana={1}{U} @@ -12189,6 +12190,7 @@ type=Sorcery [/card] [card] name=Blood Tribute +abilities=hasotherkicker target=opponent auto=lifeleech:-halfdownopponentlifetotal targetedplayer text=Kicker - Tap an untapped Vampire you control. (You may tap a Vampire you control in addition to any other costs as you cast this spell.) -- Target opponent loses half his or her life, rounded up. If Blood Tribute was kicked, you gain life equal to the life lost this way. @@ -12974,6 +12976,7 @@ toughness=2 [/card] [card] name=Bog Down +abilities=hasotherkicker target=player auto=ability$!name(discard 2 cards) target(<2>*|myhand) reject!$ targetedplayer auto=alternative ability$!target(*|myhand) reject!$ targetedplayer @@ -31432,6 +31435,7 @@ type=Enchantment [/card] [card] name=Dralnu's Pet +abilities=hasotherkicker other={3}{B}{U}{U}{D(other creature|myhand)} name(Kicker) auto=if paid(alternative) then counter(1/1,storedmanacost) && transforms((,flying)) forever text=Kicker - {2}{B}, Discard a creature card. (You may pay {2}{B} and discard a creature card in addition to any other costs as you cast this spell.) -- If Dralnu's Pet was kicked, it enters the battlefield with flying and with X +1/+1 counters on it, where X is the discarded card's converted mana cost. @@ -33188,6 +33192,7 @@ type=Land [/card] [card] name=Dwarven Landslide +abilities=hasotherkicker target=land auto=destroy other={4}{R}{R}{S(land|myBattlefield)} name(Pay Kicker) @@ -34385,8 +34390,8 @@ toughness=4 [/card] [card] name=Elemental Appeal -other={5}{R}{R}{R}{R} name(Pay Kicker) -auto=token(Elemental,Creature Elemental,7/1,red,trample,haste,unearth) and!( if paid(alternative) then 7/0 ueot )! +kicker={5} +auto=token(Elemental,Creature Elemental,7/1,red,trample,haste,unearth) and!( if paid(kicker) then 7/0 ueot )! text=Kicker {5} (You may pay an additional {5} as you cast this spell.) -- Put a 7/1 red Elemental creature token with trample and haste onto the battlefield. Exile it at the beginning of the next end step. If Elemental Appeal was kicked, that creature gets +7/+0 until end of turn. mana={R}{R}{R}{R} type=Sorcery @@ -61396,7 +61401,7 @@ toughness=2 [/card] [card] name=Kangee, Aerie Keeper -abilities=flying +abilities=flying,hasotherkicker other={X}{4}{W}{U} name(Kicker) auto=if paid(alternative) then counter(0/0,X,Feather) auto=thisforeach(counter{0/0.1.Feather}) lord(other creature[bird]) 1/1 @@ -69432,6 +69437,7 @@ type=Enchantment [/card] [card] name=Magma Burst +abilities=hasotherkicker target=creature,player auto=damage:3 auto=alternative damage:3 target(other creature,player) @@ -86179,6 +86185,7 @@ type=Enchantment [/card] [card] name=Phyrexian Scuta +abilities=hasotherkicker text=Kicker - Pay 3 life. (You may pay 3 life in addition to any other costs as you cast this spell.) -- If Phyrexian Scuta was kicked, it enters the battlefield with two +1/+1 counters on it. auto=alternative counter(1/1,2) all(this) other={3}{B}{L:3} name(Pay Kicker) @@ -88280,6 +88287,7 @@ subtype=Aura [/card] [card] name=Primal Growth +abilities=hasotherkicker aicode=activate transforms((,newability[ifnot paid(alternative) then target(land[basic]|mylibrary) moveto(mybattlefield)],newability[if paid(alternative) then target(<2>land[basic]|mylibrary) moveto(mybattlefield)])) ueot auto=ifnot paid(alternative) then name(search card) reveal:plibrarycount optionone name(choose card) target(land[basic]|reveal) moveto(ownerlibrary) and!( becomes(tobecast) ueot )! optiononeend optiontwo name(put back) target(<1>*|reveal) moveto(ownerlibrary) and!( all(*|reveal) moveto(ownerlibrary) and!(shuffle)! )! optiontwoend afterrevealed all(tobecast|mylibrary) moveto(ownerlibrary) and!(moveTo(myBattlefield) and!(tap(noevent))!)! afterrevealedend revealend auto=if paid(alternative) then name(search card) reveal:plibrarycount optionone name(choose card) target(land[basic]|reveal) moveto(ownerlibrary) and!( becomes(tobecast) ueot )! optiononeend optiontwo name(put back) target(<1>*|reveal) moveto(ownerlibrary) and!( all(*|reveal) moveto(ownerlibrary) and!(shuffle)! )! optiontwoend afterrevealed all(tobecast|mylibrary) moveto(ownerlibrary) and!(moveTo(myBattlefield) and!(tap(noevent))!)! afterrevealedend revealend @@ -97363,6 +97371,7 @@ type=Instant [/card] [card] name=Rushing River +abilities=hasotherkicker target=*[-land]|battlefield auto=moveto(ownerhand) auto=alternative target(*[-land]|battlefield) moveto(ownerhand) @@ -97836,9 +97845,9 @@ toughness=2 name=Sadistic Sacrament target=player aicode=activate transforms((,newability[if paid(alternative) then moveto(exile) target(*|targetedpersonslibrary)],newability[ifnot paid(alternative) then moveto(exile) target(*|targetedpersonslibrary)])) ueot -auto=if paid(alternative) then name(search card) Reveal:type:*:targetedpersonslibrary revealzone(targetedpersonslibrary) optionone name(choose card) target(*|reveal) transforms((,newability[all(other *|reveal) moveto(ownerlibrary) and!(shuffle)!],newability[moveto(exile)])) optiononeend optiontwo name(shuffle) bottomoflibrary target(<1>*|reveal) and!( all(*|reveal) bottomoflibrary and!(shuffle)! )! optiontwoend revealend -auto=ifnot paid(alternative) then name(search card) Reveal:type:*:targetedpersonslibrary revealzone(targetedpersonslibrary) optionone name(choose card) target(*|reveal) transforms((,newability[all(other *|reveal) moveto(ownerlibrary) and!(shuffle)!],newability[moveto(exile)])) optiononeend optiontwo name(shuffle) bottomoflibrary target(<1>*|reveal) and!( all(*|reveal) bottomoflibrary and!(shuffle)! )! optiontwoend revealend -other={7}{B}{B}{B} name(Kicker) +auto=if paid(kicker) then name(search card) Reveal:type:*:targetedpersonslibrary revealzone(targetedpersonslibrary) optionone name(choose card) target(*|reveal) transforms((,newability[all(other *|reveal) moveto(ownerlibrary) and!(shuffle)!],newability[moveto(exile)])) optiononeend optiontwo name(shuffle) bottomoflibrary target(<1>*|reveal) and!( all(*|reveal) bottomoflibrary and!(shuffle)! )! optiontwoend revealend +auto=ifnot paid(kicker) then name(search card) Reveal:type:*:targetedpersonslibrary revealzone(targetedpersonslibrary) optionone name(choose card) target(*|reveal) transforms((,newability[all(other *|reveal) moveto(ownerlibrary) and!(shuffle)!],newability[moveto(exile)])) optiononeend optiontwo name(shuffle) bottomoflibrary target(<1>*|reveal) and!( all(*|reveal) bottomoflibrary and!(shuffle)! )! optiontwoend revealend +kicker={7} text=Kicker {7} (You may pay an additional {7} as you cast this spell.) -- Search target player's library for up to three cards, exile them, then that player shuffles his or her library. If Sadistic Sacrament was kicked, instead search that player's library for up to fifteen cards, exile them, then that player shuffles his or her library. mana={B}{B}{B} type=Sorcery @@ -110469,9 +110478,9 @@ toughness=5 [card] name=Sphinx of Lost Truths abilities=flying -other={4}{U}{U}{U} name(pay kicker) +kicker={1}{U} auto=draw:3 -auto=ifnot paid(alternative) then reject notatarget(<3>*|myhand) +auto=ifnot paid(kicker) then reject notatarget(<3>*|myhand) text=Kicker {1}{U} (You may pay an additional {1}{U} as you cast this spell.) -- Flying -- When Sphinx of Lost Truths enters the battlefield, draw three cards. Then if it wasn't kicked, discard three cards. mana={3}{U}{U} type=Creature diff --git a/projects/mtg/include/CardDescriptor.h b/projects/mtg/include/CardDescriptor.h index 383a35439..1e1aac61d 100644 --- a/projects/mtg/include/CardDescriptor.h +++ b/projects/mtg/include/CardDescriptor.h @@ -41,9 +41,12 @@ class CardDescriptor: public MTGCardInstance int convertedManacost; // might fit better into MTGCardInstance? int zposComparisonMode; int zposition; + int hasKickerCost; int anyCounter; int init(); CardDescriptor(); + void unsecureSetKicked(int i); + void unsecureSetHasKickerCost(int i); void unsecureSetTapped(int i); void unsecuresetfresh(int k); void unsecuresetrecent(int j); diff --git a/projects/mtg/include/MTGDefinitions.h b/projects/mtg/include/MTGDefinitions.h index a018a85db..36247e916 100644 --- a/projects/mtg/include/MTGDefinitions.h +++ b/projects/mtg/include/MTGDefinitions.h @@ -286,7 +286,8 @@ class Constants MENTOR = 160, PROWESS = 161, NOFIZZLEALTERNATIVE = 162, - NB_BASIC_ABILITIES = 163, + HASOTHERKICKER = 163, + NB_BASIC_ABILITIES = 164, RARITY_S = 'S', //Special Rarity RARITY_M = 'M', //Mythics diff --git a/projects/mtg/src/CardDescriptor.cpp b/projects/mtg/src/CardDescriptor.cpp index 02f6baec6..105633b48 100644 --- a/projects/mtg/src/CardDescriptor.cpp +++ b/projects/mtg/src/CardDescriptor.cpp @@ -20,6 +20,7 @@ CardDescriptor::CardDescriptor() convertedManacost = -1; zposComparisonMode = COMPARISON_NONE; zposition = -1; + hasKickerCost = 0; compareName =""; nameComparisonMode = COMPARISON_NONE; colorComparisonMode = COMPARISON_NONE; @@ -50,6 +51,16 @@ int CardDescriptor::init() return result; } +void CardDescriptor::unsecureSetKicked(int k) +{ + kicked = k; +} + +void CardDescriptor::unsecureSetHasKickerCost(int k) +{ + hasKickerCost = k; +} + void CardDescriptor::unsecureSetTapped(int i) { tapped = i; @@ -220,6 +231,15 @@ MTGCardInstance * CardDescriptor::match(MTGCardInstance * card) if (excludedSet.any()) return NULL; + if ((kicked == -1 && card->kicked) || (kicked == 1 && !card->kicked)) + { + match = NULL; + } + + if ((hasKickerCost == -1 && card->getManaCost()->getKicker()) || (hasKickerCost == 1 && !card->getManaCost()->getKicker())) + { + match = NULL; + } if ((tapped == -1 && card->isTapped()) || (tapped == 1 && !card->isTapped())) { diff --git a/projects/mtg/src/CardGui.cpp b/projects/mtg/src/CardGui.cpp index cd04f9fd3..896afea8e 100644 --- a/projects/mtg/src/CardGui.cpp +++ b/projects/mtg/src/CardGui.cpp @@ -1484,8 +1484,32 @@ bool CardGui::FilterCard(MTGCard * _card,string filter) { cd.unsecureSetTapped(1); } - //Token } + //Has been kicked + else if (attribute.find("kicked") != string::npos) + { + if (minus) + { + cd.unsecureSetKicked(-1); + } + else + { + cd.unsecureSetKicked(1); + } + } + //Has kicker cost + else if (attribute.find("haskicker") != string::npos) + { + if (minus) + { + cd.unsecureSetHasKickerCost(-1); + } + else + { + cd.unsecureSetHasKickerCost(1); + } + } + //Token else if (attribute.find("token") != string::npos) { if (minus) @@ -1496,8 +1520,8 @@ bool CardGui::FilterCard(MTGCard * _card,string filter) { cd.isToken = 1; } - //put in its zone this turn } + //put in its zone this turn else if (attribute.find("fresh") != string::npos) { if (minus) diff --git a/projects/mtg/src/MTGDefinitions.cpp b/projects/mtg/src/MTGDefinitions.cpp index 1a60cd259..c7f720025 100644 --- a/projects/mtg/src/MTGDefinitions.cpp +++ b/projects/mtg/src/MTGDefinitions.cpp @@ -193,7 +193,8 @@ const char* Constants::MTGBasicAbilities[] = { "adventure", //it can be adventure "mentor", "prowess", - "nofizzle alternative" // No fizzle if paid with alternative cost (es. Zendikar Rising Modal Double Faced cards). + "nofizzle alternative", //No fizzle if paid with alternative cost (es. Zendikar Rising Modal Double Faced cards). + "hasotherkicker" //Kicker cost is expressed with "other" keyword (es. not mana kicker such as life and/or tap a creature) }; map Constants::MTGBasicAbilitiesMap; diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index 00bd6e17e..c6e44252f 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -483,10 +483,11 @@ int MTGPutInPlayRule::reactToClick(MTGCardInstance * card) ManaCost * previousManaPool = NEW ManaCost(player->getManaPool()); int payResult = player->getManaPool()->pay(card->getManaCost()); - if (card->getManaCost()->getKicker() && (OptionKicker::KICKER_ALWAYS == options[Options::KICKERPAYMENT].number || card->controller()->isAI())) + if (card->getManaCost()->getKicker() && (card->kicked || OptionKicker::KICKER_ALWAYS == options[Options::KICKERPAYMENT].number || card->controller()->isAI())) { ManaCost * withKickerCost= NEW ManaCost(card->getManaCost()); withKickerCost->add(withKickerCost->getKicker()); + card->kicked = 0; if (card->getManaCost()->getKicker()->isMulti) { while(previousManaPool->canAfford(withKickerCost)) @@ -497,11 +498,14 @@ int MTGPutInPlayRule::reactToClick(MTGCardInstance * card) for(int i = 0;i < card->kicked;i++) player->getManaPool()->pay(card->getManaCost()->getKicker()); payResult = ManaCost::MANA_PAID_WITH_KICKER; + card->alternateCostPaid[ManaCost::MANA_PAID_WITH_KICKER] = 1; } else if (previousManaPool->canAfford(withKickerCost)) { player->getManaPool()->pay(card->getManaCost()->getKicker()); payResult = ManaCost::MANA_PAID_WITH_KICKER; + card->kicked = 1; + card->alternateCostPaid[ManaCost::MANA_PAID_WITH_KICKER] = 1; } delete withKickerCost; } @@ -633,6 +637,7 @@ int MTGKickerRule::reactToClick(MTGCardInstance * card) { if (!game->targetListIsSet(card)) { + card->kicked = 1; return 0; } } @@ -649,6 +654,7 @@ int MTGKickerRule::reactToClick(MTGCardInstance * card) { ManaCost * withKickerCost= NEW ManaCost(card->getManaCost()); withKickerCost->add(withKickerCost->getKicker()); + card->kicked = 0; if (card->getManaCost()->getKicker()->isMulti) { while(previousManaPool->canAfford(withKickerCost)) @@ -659,11 +665,14 @@ int MTGKickerRule::reactToClick(MTGCardInstance * card) for(int i = 0;i < card->kicked;i++) player->getManaPool()->pay(card->getManaCost()->getKicker()); payResult = ManaCost::MANA_PAID_WITH_KICKER; + card->alternateCostPaid[ManaCost::MANA_PAID_WITH_KICKER] = 1; } else if (previousManaPool->canAfford(withKickerCost)) { player->getManaPool()->pay(card->getManaCost()->getKicker()); payResult = ManaCost::MANA_PAID_WITH_KICKER; + card->kicked = 1; + card->alternateCostPaid[ManaCost::MANA_PAID_WITH_KICKER] = 1; } delete withKickerCost; } @@ -722,7 +731,6 @@ int MTGKickerRule::reactToClick(MTGCardInstance * card) copy->castX = copy->X; } } - return 1; } diff --git a/projects/mtg/src/TargetChooser.cpp b/projects/mtg/src/TargetChooser.cpp index 5d380f540..01e555e72 100644 --- a/projects/mtg/src/TargetChooser.cpp +++ b/projects/mtg/src/TargetChooser.cpp @@ -481,8 +481,32 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta { cd->unsecureSetTapped(1); } - //Token } + //Has been kicked + else if (attribute.find("kicked") != string::npos) + { + if (minus) + { + cd->unsecureSetKicked(-1); + } + else + { + cd->unsecureSetKicked(1); + } + } + //Has kicker cost + else if (attribute.find("haskicker") != string::npos) + { + if (minus) + { + cd->unsecureSetHasKickerCost(-1); + } + else + { + cd->unsecureSetHasKickerCost(1); + } + } + //Token else if (attribute.find("token") != string::npos) { if (minus) @@ -493,8 +517,8 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta { cd->isToken = 1; } - //put in its zone this turn } + //put in its zone this turn else if (attribute.find("fresh") != string::npos) { if (minus)