Fix Fatal Lore (#4488)

Co-authored-by: tool4EvEr <tool4EvEr@192.168.0.59>
This commit is contained in:
tool4ever
2024-01-08 07:12:49 +01:00
committed by GitHub
parent 6db188406a
commit 1a3a5cbf8c
9 changed files with 36 additions and 22 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

@@ -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

@@ -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

@@ -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;
}