Merge branch 'Card-Forge:master' into master

This commit is contained in:
TabletopGeneral
2024-01-08 06:57:03 -05:00
committed by GitHub
17 changed files with 105 additions and 23 deletions

View File

@@ -37,11 +37,6 @@ public class CharmAi extends SpellAbilityAi {
min = sa.hasParam("MinCharmNum") ? AbilityUtils.calculateAmount(source, sa.getParam("MinCharmNum"), sa) : num;
}
// only randomize if not all possible together
if (num < choices.size() || source.hasKeyword(Keyword.ESCALATE)) {
Collections.shuffle(choices);
}
boolean timingRight = sa.isTrigger(); //is there a reason to play the charm now?
// Reset the chosen list otherwise it will be locked in forever by earlier calls
@@ -51,11 +46,15 @@ public class CharmAi extends SpellAbilityAi {
if (!ai.equals(sa.getActivatingPlayer())) {
// This branch is for "An Opponent chooses" Charm spells from Alliances
// Current just choose the first available spell, which seem generally less disastrous for the AI.
//return choices.subList(0, 1);
chosenList = choices.subList(1, choices.size());
} else if ("Triskaidekaphobia".equals(ComputerUtilAbility.getAbilitySourceName(sa))) {
chosenList = chooseTriskaidekaphobia(choices, ai);
} else {
// only randomize if not all possible together
if (num < choices.size() || source.hasKeyword(Keyword.ESCALATE)) {
Collections.shuffle(choices);
}
/*
* The generic chooseOptionsAi uses canPlayAi() to determine good choices
* which means most "bonus" effects like life-gain and random pumps will

View File

@@ -916,6 +916,7 @@ public final class GameActionUtil {
if (ability.getApi() == ApiType.Charm) {
// reset chain
ability.setSubAbility(null);
ability.setChosenList(null);
}
ability.clearTargets();

View File

@@ -1050,7 +1050,7 @@ public class AbilityUtils {
addPlayer(card.getRemembered(), defined, players);
}
else if (defined.startsWith("Imprinted")) {
addPlayer(Lists.newArrayList(card.getImprintedCards()), defined, players);
addPlayer(card.getImprintedCards(), defined, players);
}
else if (defined.startsWith("EffectSource")) {
Card root = findEffectRoot(card);
@@ -1093,9 +1093,9 @@ public class AbilityUtils {
o = ((SpellAbility) c).getActivatingPlayer();
} else if (c instanceof Iterable<?>) { // For merged permanent
if (orCont) {
addPlayer(ImmutableList.copyOf(Iterables.filter((Iterable<Object>)c, Player.class)), "", players);
addPlayer(Iterables.filter((Iterable<Object>)c, Player.class), "", players);
}
addPlayer(ImmutableList.copyOf(Iterables.filter((Iterable<Object>)c, Card.class)), "Controller", players);
addPlayer(Iterables.filter((Iterable<Object>)c, Card.class), "Controller", players);
}
}
else if (defParsed.endsWith("Opponent")) {
@@ -1205,6 +1205,9 @@ public class AbilityUtils {
else if (defined.equals("DefendingPlayer")) {
players.add(game.getCombat().getDefendingPlayerRelatedTo(card));
}
else if (defined.equals("ChoosingPlayer")) {
players.add(((SpellAbility) sa).getRootAbility().getChoosingPlayer());
}
else if (defined.equals("ChosenPlayer")) {
final Player p = card.getChosenPlayer();
if (p != null) {
@@ -1212,7 +1215,7 @@ public class AbilityUtils {
}
}
else if (defined.startsWith("ChosenCard")) {
addPlayer(Lists.newArrayList(card.getChosenCards()), defined, players);
addPlayer(card.getChosenCards(), defined, players);
}
else if (defined.equals("SourceController")) {
players.add(sa.getHostCard().getController());
@@ -3053,11 +3056,11 @@ public class AbilityUtils {
return applyAbilityTextChangeEffects(val, ability);
}
private static void addPlayer(Iterable<Object> objects, final String def, FCollection<Player> players) {
private static void addPlayer(Iterable<?> objects, final String def, FCollection<Player> players) {
addPlayer(objects, def, players, false);
}
private static void addPlayer(Iterable<Object> objects, final String def, FCollection<Player> players, boolean skipRemembered) {
private static void addPlayer(Iterable<?> objects, final String def, FCollection<Player> players, boolean skipRemembered) {
for (Object o : objects) {
if (o instanceof Player) {
final Player p = (Player) o;

View File

@@ -224,7 +224,7 @@ public class CharmEffect extends SpellAbilityEffect {
//String choosers = sa.getParam("Chooser");
FCollection<Player> opponents = activator.getOpponents(); // all cards have Choser$ Opponent, so it's hardcoded here
chooser = activator.getController().chooseSingleEntityForEffect(opponents, sa, "Choose an opponent", null);
source.setChosenPlayer(chooser);
sa.setChoosingPlayer(chooser);
}
List<AbilitySub> chosen = chooser.getController().chooseModeForAbility(sa, choices, min, num, canRepeat);

View File

@@ -83,7 +83,7 @@ public class CardFactory {
out = assignNewId ? getCard(in.getPaperCard(), in.getOwner(), in.getGame())
: getCard(in.getPaperCard(), in.getOwner(), in.getId(), in.getGame());
} else { // token
out = CardFactory.copyStats(in, in.getController(), assignNewId);
out = copyStats(in, in.getController(), assignNewId);
out.setToken(true);
// need to copy this values for the tokens
@@ -128,7 +128,7 @@ public class CardFactory {
int id = game.nextCardId();
// need to create a physical card first, i need the original card faces
final Card copy = CardFactory.getCard(original.getPaperCard(), controller, id, game);
final Card copy = getCard(original.getPaperCard(), controller, id, game);
if (original.isTransformable()) {
// 707.8a If an effect creates a token that is a copy of a transforming permanent or a transforming double-faced card not on the battlefield,
@@ -530,7 +530,7 @@ public class CardFactory {
c.setSetCode(in.getSetCode());
for (final CardStateName state : in.getStates()) {
CardFactory.copyState(in, state, c, state);
copyState(in, state, c, state);
}
c.setState(in.getCurrentStateName(), false);

View File

@@ -103,11 +103,14 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
// choices for constructor isPermanent argument
private String originalDescription = "", description = "";
private String originalStackDescription = "", stackDescription = "";
private ManaCost multiKickerManaCost;
private Player activatingPlayer;
private Player targetingPlayer;
private Player choosingPlayer;
private Pair<Long, Player> controlledByPlayer;
private ManaCostBeingPaid manaCostBeingPaid;
private ManaCost multiKickerManaCost;
private int spentPhyrexian = 0;
private int paidLifeAmount = 0;
@@ -147,7 +150,6 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private TreeBasedTable<String, Boolean, CardCollection> paidLists = TreeBasedTable.create();
private EnumMap<AbilityKey, Object> triggeringObjects = AbilityKey.newMap();
private EnumMap<AbilityKey, Object> replacingObjects = AbilityKey.newMap();
private final List<String> pipsToReduce = new ArrayList<>();
@@ -481,6 +483,13 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
targetingPlayer = targetingPlayer0;
}
public Player getChoosingPlayer() {
return choosingPlayer;
}
public void setChoosingPlayer(Player choosingPlayer0) {
choosingPlayer = choosingPlayer0;
}
/**
* @return returns who controls the controller of this sa when it is resolving (for Word of Command effect). Null means not being controlled by other
*/

View File

@@ -126,4 +126,6 @@ March of the Machine Jumpstart, -/2/MOM, Meta-Choose(S(MOM Brood 1)Brood 1;S(MOM
The Lord of the Rings: Tales of Middle-earth, 3/6/LTR, LTR
The Lord of the Rings: Tales of Middle-earth Jumpstart, -/2/LTR, Meta-Choose(S(LTR Courageous 1)Courageous 1;S(LTR Courageous 2)Courageous 2;S(LTR Tricksy 1)Tricksy 1;S(LTR Tricksy 2)Tricksy 2;S(LTR Mordor 1)Mordor 1;S(LTR Mordor 2)Mordor 1;S(LTR Marauders 1)Marauders 1;S(LTR Marauders 2)Marauders 1;S(LTR Journey 1)Journey 1;S(LTR Journey 2)Journey 2)Themes
Wilds of Eldraine, 3/6/WOE, WOE
The Lost Caverns of Ixalan, 3/6/LCI, LCI
Alchemy: Eldraine,3/6/WOE, YWOE
The Lost Caverns of Ixalan, 3/6/LCI, LCI
Alchemy: Ixalan,3/6/LCI, YLCI

View File

@@ -0,0 +1,11 @@
Name:Ensnared by the Mara
ManaCost:2 R R
Types:Sorcery
A:SP$ VillainousChoice | Defined$ Opponent | Choices$ DBDig,DBDamage | SpellDescription$ Each opponent faces a villainous choice — They exile cards from the top of their library until they exile a nonland card, then you may cast that card without paying its mana cost, or that player exiles the top four cards of their library and CARDNAME deals damage equal to the total mana value of those exiled cards to that player.
SVar:DBDig:DB$ DigUntil | Defined$ Remembered | Valid$ Card.nonLand | FoundDestination$ Exile | RevealedDestination$ Exile | RememberFound$ True | SubAbility$ DBPlay | SpellDescription$ They exile cards from the top of their library until they exile a nonland card, then you may cast that card without paying its mana cost.
SVar:DBPlay:DB$ Play | Controller$ You | Defined$ Remembered | WithoutManaCost$ True | ValidSA$ Spell | Optional$ True | Amount$ All | SubAbility$ DBCleanup
SVar:DBDamage:DB$ Dig | RememberChanged$ True | DestinationZone$ Exile | Defined$ Remembered | DigNum$ 4 | ChangeNum$ All | SubAbility$ DamageOpponent | SpellDescription$ That player exiles the top four cards of their library and CARDNAME deals damage equal to the total mana value of those exiled cards to that player.
SVar:DamageOpponent:DB$ DealDamage | Defined$ Remembered | NumDmg$ X | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:Remembered$SumCMC
Oracle:Each opponent faces a villainous choice — They exile cards from the top of their library until they exile a nonland card, then you may cast that card without paying its mana cost, or that player exiles the top four cards of their library and Ensnared by the Mara deals damage equal to the total mana value of those exiled cards to that player.

View File

@@ -3,7 +3,7 @@ ManaCost:2 B B
Types:Sorcery
A:SP$ Charm | Cost$ 2 B B | Chooser$ Opponent | Choices$ DrawThree,DestroyAndDraw
SVar:DrawThree:DB$ Draw | NumCards$ 3 | Defined$ You | SpellDescription$ You draw three cards.
SVar:DestroyAndDraw:DB$ Destroy | ValidTgts$ Creature.ChosenCtrl | TgtPrompt$ Select target creature | TargetMin$ 0 | TargetMax$ 2 | NoRegen$ True | SpellDescription$ You destroy up to two target creatures that opponent controls and that player draws up to three cards. Those creatures can't be regenerated. | SubAbility$ ChooserDraws
SVar:ChooserDraws:DB$ Draw | NumCards$ 3 | Defined$ ChosenPlayer | UpTo$ True
SVar:DestroyAndDraw:DB$ Destroy | ValidTgts$ Creature.ControlledBy ChoosingPlayer | TgtPrompt$ Select target creature | TargetMin$ 0 | TargetMax$ 2 | NoRegen$ True | SpellDescription$ You destroy up to two target creatures that opponent controls and that player draws up to three cards. Those creatures can't be regenerated. | SubAbility$ ChooserDraws
SVar:ChooserDraws:DB$ Draw | NumCards$ 3 | Defined$ ChoosingPlayer | UpTo$ True
AI:RemoveDeck:All
Oracle:An opponent chooses one —\n• You draw three cards.\n• You destroy up to two target creatures that player controls. They can't be regenerated. That player draws up to three cards.

View File

@@ -0,0 +1,14 @@
Name:Genesis of the Daleks
ManaCost:4 B B
Types:Enchantment Saga
K:Chapter:4:DBToken,DBToken,DBToken,DBVillainous
SVar:DBToken:DB$ Token | TokenAmount$ X | TokenScript$ b_3_3_a_dalek_menace | SpellDescription$ Create a 3/3 black Dalek artifact creature token with menace for each lore counter on CARDNAME.
SVar:DBVillainous:DB$ VillainousChoice | Choices$ DBDestroyDalek,DBDestroyNonDalek | ValidTgts$ Opponent | SpellDescription$ Target opponent faces a villainous choice — Destroy all Dalek creatures and each of your opponents loses life equal to the total power of Daleks that died this turn, or destroy all non-Dalek creatures.
SVar:DBDestroyDalek:DB$ DestroyAll | ValidCards$ Creature.Dalek | SubAbility$ DBLoseLife | SpellDescription$ Destroy all Dalek creatures and each of your opponents loses life equal to the total power of Daleks that died this turn.
SVar:DBLoseLife:DB$ LoseLife | LifeAmount$ Y | Defined$ Opponent
SVar:DBDestroyNonDalek:DB$ DestroyAll | ValidCards$ Creature.nonDalek | SpellDescription$ Destroy all non-Dalek creatures.
SVar:X:Count$CardCounters.LORE
SVar:Y:Count$ThisTurnEntered_Graveyard_from_Battlefield_Dalek$CardPower
DeckHas:Ability$Token & Type$Artifact|Dalek
DeckHints:Type$Dalek
Oracle:(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)\nI, II, III — Create a 3/3 black Dalek artifact creature token with menace for each lore counter on Genesis of the Daleks.\nIV — Target opponent faces a villainous choice — Destroy all Dalek creatures and each of your opponents loses life equal to the total power of Daleks that died this turn, or destroy all non-Dalek creatures.

View File

@@ -0,0 +1,10 @@
Name:Hunted by The Family
ManaCost:5 U U
Types:Sorcery
A:SP$ ChooseCard | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Select up to four target creatures you don't control | TargetMin$ 0 | TargetMax$ 4 | SubAbility$ DBRepeat | SpellDescription$ Choose up to four target creatures you don't control. For each of them, that creature's controller faces a villainous choice — That creature becomes a 1/1 white Human creature and loses all abilities, or you create a token that's a copy of it.
SVar:DBRepeat:DB$ RepeatEach | RepeatSubAbility$ DBChoice | DefinedCards$ Targeted
SVar:DBChoice:DB$ VillainousChoice | Defined$ RememberedController | Choices$ DBAnimate,DBCopy
SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Power$ 1 | Toughness$ 1 | Types$ Creature,Human | RemoveCreatureTypes$ True | RemoveCardTypes$ True | RemoveAllAbilities$ True | IsCurse$ True | Colors$ White | OverwriteColors$ True | Duration$ Permanent | SpellDescription$ That creature becomes a 1/1 white Human creature and loses all abilities.
SVar:DBCopy:DB$ CopyPermanent | Defined$ Remembered | SpellDescription$ You create a token that's a copy of it.
DeckHas:Ability$Token & Type$Human
Oracle:Choose up to four target creatures you don't control. For each of them, that creature's controller faces a villainous choice — That creature becomes a 1/1 white Human creature and loses all abilities, or you create a token that's a copy of it.

View File

@@ -0,0 +1,15 @@
Name:Midnight Crusader Shuttle
ManaCost:4
Types:Artifact Vehicle
PT:3/4
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigVillainousChoice | TriggerDescription$ Midnight Entity — Whenever CARDNAME attacks, defending player faces a villainous choice — That player sacrifices a creature, or you gain control of a creature of your choice that player controls until end of turn. If you gain control of a creature this way, tap it, and it's attacking that player.
SVar:TrigVillainousChoice:DB$ VillainousChoice | Defined$ TriggeredDefendingPlayer | Choices$ DBSacrifice,DBGainControl
SVar:DBSacrifice:DB$ Sacrifice | Amount$ 1 | SacValid$ Creature | Defined$ Remembered | SpellDescription$ That player sacrifices a creature.
SVar:DBGainControl:DB$ GainControl | Defined$ ChosenCard | Choices$ Creature.RememberedPlayerCtrl | ChoiceTitle$ Select a creature that player controls | NewController$ You | LoseControl$ EOT | RememberControlled$ True | SubAbility$ DBTap | SpellDescription$ You gain control of a creature of your choice that player controls until end of turn. If you gain control of a creature this way, tap it, and it's attacking that player.
SVar:DBTap:DB$ Tap | Defined$ Remembered | ConditionDefined$ Remembered | ConditionPresent$ Card.YouCtrl | SubAbility$ DBSetAttacking
SVar:DBSetAttacking:DB$ ChangeCombatants | Defined$ Remembered | ConditionDefined$ Remembered | ConditionPresent$ Card.YouCtrl | Attacking$ RememberedPlayer | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
K:Crew:2
SVar:HasAttackEffect:True
DeckHas:Ability$Sacrifice
Oracle:Midnight Entity — Whenever Midnight Crusader Shuttle attacks, defending player faces a villainous choice — That player sacrifices a creature, or you gain control of a creature of your choice that player controls until end of turn. If you gain control of a creature this way, tap it, and it's attacking that player.\nCrew 2

View File

@@ -4,7 +4,7 @@ Types:Sorcery
A:SP$ Charm | Cost$ 1 B R G | Chooser$ Opponent | Choices$ Fortune,Misfortune
SVar:Fortune:DB$ PutCounterAll | ValidCards$ Creature.YouCtrl | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBGainLife | SpellDescription$ Put a +1/+1 counter on each creature you control. You gain 4 life. | SubAbility$ DBGainLife
SVar:DBGainLife:DB$ GainLife | LifeAmount$ 4
SVar:Misfortune:DB$ PutCounterAll | ValidCards$ Creature.ChosenCtrl | CounterType$ M1M1 | CounterNum$ 1 | SubAbility$ DBLoseLife | SpellDescription$ You put a -1/-1 counter on each creature that player controls and CARDNAME deals 4 damage to that player. | SubAbility$ DBDamage
SVar:DBDamage:DB$ DealDamage | Defined$ ChosenPlayer | NumDmg$ 4
SVar:Misfortune:DB$ PutCounterAll | ValidCards$ Creature.ControlledBy ChoosingPlayer | CounterType$ M1M1 | CounterNum$ 1 | SubAbility$ DBLoseLife | SpellDescription$ You put a -1/-1 counter on each creature that player controls and CARDNAME deals 4 damage to that player. | SubAbility$ DBDamage
SVar:DBDamage:DB$ DealDamage | Defined$ ChoosingPlayer | NumDmg$ 4
SVar:ChooserDraws:DB$ Draw | NumCards$ 3 | Defined$ ChosenPlayer
Oracle:An opponent chooses one —\n• You put a +1/+1 counter on each creature you control and gain 4 life.\n• You put a -1/-1 counter on each creature that player controls and Misfortune deals 4 damage to that player.

View File

@@ -0,0 +1,14 @@
Name:Missy
ManaCost:3 U B R
Types:Legendary Creature Time Lord Rogue
PT:4/5
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.nonArtifact+Other | TriggerZones$ Battlefield | Execute$ TrigChangeZone | TriggerDescription$ Whenever another nonartifact creature dies, return it to the battlefield under your control face down and tapped. It's a 2/2 Cyberman artifact creature.
SVar:TrigChangeZone:DB$ ChangeZone | Defined$ TriggeredCard | GainControl$ True | FaceDown$ True | Origin$ Graveyard | Destination$ Battlefield | Tapped$ True | FaceDownSetType$ Artifact & Creature & Cyberman | FaceDownPower$ 2 | FaceDownToughness$ 2
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ VillainousChoice | TriggerDescription$ At the beginning of your end step, each opponent faces a villainous choice — Each artifact creature you control deals 1 damage to that opponent, or you draw a card and chaos ensues.
SVar:VillainousChoice:DB$ VillainousChoice | Defined$ Opponent | Choices$ DBDamage,DBDraw
SVar:DBDamage:DB$ DealDamage | NumDmg$ 1 | DamageSource$ Valid Artifact.Creature+YouCtrl | Defined$ Remembered | SpellDescription$ Each artifact creature you control deals 1 damage to that opponent
SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBChaos | SpellDescription$ You draw a card and chaos ensues.
SVar:DBChaos:DB$ ChaosEnsues
DeckHints:Type$Artifact
DeckHas:Type$Cyberman
Oracle:Whenever another nonartifact creature dies, return it to the battlefield under your control face down and tapped. It's a 2/2 Cyberman artifact creature.\nAt the beginning of your end step, each opponent faces a villainous choice — Each artifact creature you control deals 1 damage to that opponent, or you draw a card and chaos ensues.

View File

@@ -3,6 +3,7 @@ Code=YWOE
Date=2023-10-10
Name=Alchemy: Eldraine
Type=Online
Booster=1 UncommonRareMythic,8 Common:fromsheet("WOE cards"), 3 Uncommon:fromSheet("WOE cards"), 1 UncommonRareMythic:fromSheet("WOT cards"), 1 RareMythic:fromSheet("WOE cards"), 1 BasicLand:fromSheet("WOE cards")
ScryfallCode=YWOE
[cards]

View File

@@ -3,6 +3,7 @@ Code=YLCI
Date=2023-12-05
Name=Alchemy: Ixalan
Type=Online
Booster=1 UncommonRareMythic,8 Common:!dfc:!fromSheet("LCI lands"):fromsheet("LCI cards"), 3 Uncommon:!dfc:!fromSheet("LCI lands"):fromSheet("LCI cards"), 1 fromSheet("LCI double-face"), 1 RareMythic:fromSheet("LCI cards"), 1 fromSheet("LCI lands")
ScryfallCode=YLCI
[cards]

View File

@@ -74,11 +74,13 @@ public class HumanPlaySpellAbility {
if ("X".equals(ability.getParam("CharmNum"))) {
// CR 601.4
if (!announceValuesLikeX()) {
game.clearTopLibsCast(ability);
return false;
}
needX = false;
}
if (!CharmEffect.makeChoices(ability)) {
game.clearTopLibsCast(ability);
// 603.3c If no mode is chosen, the ability is removed from the stack.
return false;
}