mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
Fix AI not targeting triggered Random Charm (#8955)
This commit is contained in:
@@ -377,7 +377,7 @@ public class AiController {
|
|||||||
|
|
||||||
if (card.isSaga()) {
|
if (card.isSaga()) {
|
||||||
for (final Trigger tr : card.getTriggers()) {
|
for (final Trigger tr : card.getTriggers()) {
|
||||||
if (tr.getMode() != TriggerType.CounterAdded || !tr.isChapter()) {
|
if (!tr.isChapter()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,6 +393,7 @@ public class AiController {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// usually later chapters make use of an earlier one
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1274,9 +1274,15 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean prepareSingleSa(final Card host, final SpellAbility sa, boolean isMandatory) {
|
private boolean prepareSingleSa(final Card host, SpellAbility sa, boolean isMandatory) {
|
||||||
if (sa.getApi() == ApiType.Charm) {
|
if (sa.getApi() == ApiType.Charm) {
|
||||||
return CharmEffect.makeChoices(sa);
|
if (!CharmEffect.makeChoices(sa)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!sa.hasParam("Random")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
sa = sa.getSubAbility();
|
||||||
}
|
}
|
||||||
if (sa.hasParam("TargetingPlayer")) {
|
if (sa.hasParam("TargetingPlayer")) {
|
||||||
Player targetingPlayer = AbilityUtils.getDefinedPlayers(host, sa.getParam("TargetingPlayer"), sa).get(0);
|
Player targetingPlayer = AbilityUtils.getDefinedPlayers(host, sa.getParam("TargetingPlayer"), sa).get(0);
|
||||||
|
|||||||
@@ -634,6 +634,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// not urgent, get the largest creature possible
|
// not urgent, get the largest creature possible
|
||||||
|
// TODO checkETBEffects
|
||||||
return ComputerUtilCard.getBestCreatureAI(list);
|
return ComputerUtilCard.getBestCreatureAI(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1546,10 +1547,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
if (fetchList.isEmpty()) {
|
if (fetchList.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String type = sa.getParam("ChangeType");
|
String type = sa.getParamOrDefault("ChangeType", "");
|
||||||
if (type == null) {
|
|
||||||
type = "Card";
|
|
||||||
}
|
|
||||||
|
|
||||||
Card c = null;
|
Card c = null;
|
||||||
final Player activator = sa.getActivatingPlayer();
|
final Player activator = sa.getActivatingPlayer();
|
||||||
|
|||||||
@@ -94,18 +94,21 @@ public class CharmAi extends SpellAbilityAi {
|
|||||||
private List<AbilitySub> chooseOptionsAi(SpellAbility sa, List<AbilitySub> choices, final Player ai, boolean isTrigger, int num, int min) {
|
private List<AbilitySub> chooseOptionsAi(SpellAbility sa, List<AbilitySub> choices, final Player ai, boolean isTrigger, int num, int min) {
|
||||||
List<AbilitySub> chosenList = Lists.newArrayList();
|
List<AbilitySub> chosenList = Lists.newArrayList();
|
||||||
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
||||||
boolean allowRepeat = sa.hasParam("CanRepeatModes"); // FIXME: unused for now, the AI doesn't know how to effectively handle repeated choices
|
// TODO unused for now, the AI doesn't know how to effectively handle repeated choices
|
||||||
|
boolean allowRepeat = sa.hasParam("CanRepeatModes");
|
||||||
|
|
||||||
// Pawprint
|
// Pawprint
|
||||||
final int pawprintLimit = sa.hasParam("Pawprint") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Pawprint"), sa) : 0;
|
final int pawprintLimit = sa.hasParam("Pawprint") ? AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Pawprint"), sa) : 0;
|
||||||
if (pawprintLimit > 0) {
|
if (pawprintLimit > 0) {
|
||||||
Collections.reverse(choices); // try to pay for the more expensive subs first
|
// try to pay for the more expensive subs first
|
||||||
|
Collections.reverse(choices);
|
||||||
}
|
}
|
||||||
int pawprintAmount = 0;
|
int pawprintAmount = 0;
|
||||||
|
|
||||||
// First pass using standard canPlayAi() for good choices
|
// First pass using standard canPlayAi() for good choices
|
||||||
for (AbilitySub sub : choices) {
|
for (AbilitySub sub : choices) {
|
||||||
sub.setActivatingPlayer(ai);
|
sub.setActivatingPlayer(ai);
|
||||||
|
// TODO refactor to obtain the AiAbilityDecision instead, then we can check all to sort by value
|
||||||
if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
|
if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) {
|
||||||
if (pawprintLimit > 0) {
|
if (pawprintLimit > 0) {
|
||||||
int curPawprintAmount = AbilityUtils.calculateAmount(sub.getHostCard(), sub.getParamOrDefault("Pawprint", "0"), sub);
|
int curPawprintAmount = AbilityUtils.calculateAmount(sub.getHostCard(), sub.getParamOrDefault("Pawprint", "0"), sub);
|
||||||
@@ -116,7 +119,8 @@ public class CharmAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
chosenList.add(sub);
|
chosenList.add(sub);
|
||||||
if (chosenList.size() == num) {
|
if (chosenList.size() == num) {
|
||||||
return chosenList; // maximum choices reached
|
// maximum choices reached
|
||||||
|
return chosenList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +149,8 @@ public class CharmAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chosenList.size() < min) {
|
if (chosenList.size() < min) {
|
||||||
chosenList.clear(); // not enough choices
|
// not enough choices
|
||||||
|
chosenList.clear();
|
||||||
}
|
}
|
||||||
return chosenList;
|
return chosenList;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,16 +72,14 @@ public class ChooseCardNameAi extends SpellAbilityAi {
|
|||||||
// 5 percent chance to cast per opposing card with a non mana ability
|
// 5 percent chance to cast per opposing card with a non mana ability
|
||||||
if (MyRandom.getRandom().nextFloat() <= .05 * oppPerms.size()) {
|
if (MyRandom.getRandom().nextFloat() <= .05 * oppPerms.size()) {
|
||||||
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
|
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
|
||||||
} else {
|
|
||||||
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
|
||||||
}
|
}
|
||||||
|
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mandatory) {
|
if (mandatory) {
|
||||||
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
|
return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
|
||||||
} else {
|
|
||||||
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
|
||||||
}
|
}
|
||||||
|
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
|
||||||
}
|
}
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
|
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
|
||||||
|
|||||||
@@ -1175,7 +1175,7 @@ public class CountersPutAi extends CountersAi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
|
public int chooseNumber(Player player, SpellAbility sa, int min, int max, Map<String, Object> params) {
|
||||||
if (sa.hasParam("ReadAhead")) {
|
if (sa.isKeyword(Keyword.READ_AHEAD)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return max;
|
return max;
|
||||||
|
|||||||
@@ -982,6 +982,9 @@ public class AbilityUtils {
|
|||||||
for (final Card c : getDefinedCards(card, "Targeted", sa)) {
|
for (final Card c : getDefinedCards(card, "Targeted", sa)) {
|
||||||
players.add(c.getOwner());
|
players.add(c.getOwner());
|
||||||
}
|
}
|
||||||
|
for (final SpellAbility s : getDefinedSpellAbilities(card, "Targeted", sa)) {
|
||||||
|
players.add(s.getHostCard().getOwner());
|
||||||
|
}
|
||||||
} else if (defined.equals("TargetedAndYou") && sa instanceof SpellAbility) {
|
} else if (defined.equals("TargetedAndYou") && sa instanceof SpellAbility) {
|
||||||
final SpellAbility saTargeting = ((SpellAbility)sa).getSATargetingPlayer();
|
final SpellAbility saTargeting = ((SpellAbility)sa).getSATargetingPlayer();
|
||||||
if (saTargeting != null) {
|
if (saTargeting != null) {
|
||||||
@@ -1118,10 +1121,8 @@ public class AbilityUtils {
|
|||||||
final String replacingType = defined.substring(8);
|
final String replacingType = defined.substring(8);
|
||||||
o = root.getReplacingObject(AbilityKey.fromString(replacingType));
|
o = root.getReplacingObject(AbilityKey.fromString(replacingType));
|
||||||
}
|
}
|
||||||
if (o != null) {
|
if (o instanceof Player) {
|
||||||
if (o instanceof Player) {
|
players.add((Player) o);
|
||||||
players.add((Player) o);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (defined.startsWith("Non")) {
|
} else if (defined.startsWith("Non")) {
|
||||||
players.addAll(game.getPlayersInTurnOrder());
|
players.addAll(game.getPlayersInTurnOrder());
|
||||||
|
|||||||
@@ -2496,7 +2496,7 @@ public class CardFactoryUtil {
|
|||||||
inst.addReplacement(re);
|
inst.addReplacement(re);
|
||||||
} else if (keyword.equals("Read ahead")) {
|
} else if (keyword.equals("Read ahead")) {
|
||||||
String repeffstr = "Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | Secondary$ True | ReplacementResult$ Updated | Description$ Choose a chapter and start with that many lore counters.";
|
String repeffstr = "Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | Secondary$ True | ReplacementResult$ Updated | Description$ Choose a chapter and start with that many lore counters.";
|
||||||
String effStr = "DB$ PutCounter | Defined$ Self | CounterType$ LORE | ETB$ True | UpTo$ True | UpToMin$ 1 | ReadAhead$ True | CounterNum$ FinalChapterNr";
|
String effStr = "DB$ PutCounter | Defined$ Self | CounterType$ LORE | ETB$ True | UpTo$ True | UpToMin$ 1 | CounterNum$ FinalChapterNr";
|
||||||
|
|
||||||
SpellAbility saCounter = AbilityFactory.getAbility(effStr, card);
|
SpellAbility saCounter = AbilityFactory.getAbility(effStr, card);
|
||||||
saCounter.setSVar("FinalChapterNr", "Count$FinalChapterNr");
|
saCounter.setSVar("FinalChapterNr", "Count$FinalChapterNr");
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Types:Artifact
|
|||||||
K:Flash
|
K:Flash
|
||||||
K:ETBReplacement:Other:DBNameCard
|
K:ETBReplacement:Other:DBNameCard
|
||||||
SVar:DBNameCard:DB$ NameCard | Defined$ You | SpellDescription$ As CARDNAME enters, choose a card name.
|
SVar:DBNameCard:DB$ NameCard | Defined$ You | SpellDescription$ As CARDNAME enters, choose a card name.
|
||||||
S:Mode$ RaiseCost | EffectZone$ Battlefield | ValidCard$ Card.NamedCard | Type$ Spell | Activator$ Player | Amount$ 3 | Description$ Spells with the chosen name cost 3 more to cast.
|
S:Mode$ RaiseCost | EffectZone$ Battlefield | ValidCard$ Card.NamedCard | Type$ Spell | Activator$ Player | Amount$ 3 | Description$ Spells with the chosen name cost {3} more to cast.
|
||||||
S:Mode$ CantBeActivated | ValidCard$ Card.NamedCard | ValidSA$ Activated.!ManaAbility | Description$ Activated abilities of sources with the chosen name can't be activated unless they're mana abilities.
|
S:Mode$ CantBeActivated | ValidCard$ Card.NamedCard | ValidSA$ Activated.!ManaAbility | Description$ Activated abilities of sources with the chosen name can't be activated unless they're mana abilities.
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
Oracle:Flash\nAs Disruptor Flute enters, choose a card name.\nSpells with the chosen name cost 3 more to cast.\nActivated abilities of sources with the chosen name can't be activated unless they're mana abilities.
|
Oracle:Flash\nAs Disruptor Flute enters, choose a card name.\nSpells with the chosen name cost {3} more to cast.\nActivated abilities of sources with the chosen name can't be activated unless they're mana abilities.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ PT:2/2
|
|||||||
K:Trample
|
K:Trample
|
||||||
K:etbCounter:P1P1:X:no Condition:NICKNAME enters with a +1/+1 counter on him for each land you control.
|
K:etbCounter:P1P1:X:no Condition:NICKNAME enters with a +1/+1 counter on him for each land you control.
|
||||||
SVar:X:Count$Valid Land.YouCtrl
|
SVar:X:Count$Valid Land.YouCtrl
|
||||||
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigChangeZone | TriggerDescription$ When NICKNAME dies, return this card to your hand.
|
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigReturn | TriggerDescription$ When NICKNAME dies, return this card to your hand.
|
||||||
SVar:TrigReturn:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Hand
|
SVar:TrigReturn:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Hand
|
||||||
DeckHas:Ability$Counters
|
DeckHas:Ability$Counters
|
||||||
Oracle:Trample (This creature can deal excess combat damage to the player it's attacking.)\nMichelangelo enters with a +1/+1 counter on him for each land you control.\nWhen Michelangelo dies, return this card to your hand.
|
Oracle:Trample (This creature can deal excess combat damage to the player it's attacking.)\nMichelangelo enters with a +1/+1 counter on him for each land you control.\nWhen Michelangelo dies, return this card to your hand.
|
||||||
Reference in New Issue
Block a user