mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
Logic tweak
This commit is contained in:
@@ -1312,7 +1312,6 @@ public class ComputerUtilCard {
|
|||||||
final int power, final List<String> keywords) {
|
final int power, final List<String> keywords) {
|
||||||
return shouldPumpCard(ai, sa, c, toughness, power, keywords, false);
|
return shouldPumpCard(ai, sa, c, toughness, power, keywords, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean shouldPumpCard(final Player ai, final SpellAbility sa, final Card c, final int toughness,
|
public static boolean shouldPumpCard(final Player ai, final SpellAbility sa, final Card c, final int toughness,
|
||||||
final int power, final List<String> keywords, boolean immediately) {
|
final int power, final List<String> keywords, boolean immediately) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
@@ -1546,35 +1545,37 @@ public class ComputerUtilCard {
|
|||||||
|| ("PumpForTrample".equals(sa.getParam("AILogic")))) {
|
|| ("PumpForTrample".equals(sa.getParam("AILogic")))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// try to determine if pumping a creature for more power will give lethal on board
|
// try to determine if pumping a creature for more power will give lethal on board
|
||||||
// considering all unblocked creatures after the blockers are already declared
|
// considering all unblocked creatures after the blockers are already declared
|
||||||
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && pumpedDmg > dmg) {
|
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
int totalPowerUnblocked = 0;
|
int totalPowerUnblocked = 0;
|
||||||
for (Card atk : combat.getAttackers()) {
|
for (Card atk : combat.getAttackers()) {
|
||||||
if (combat.isBlocked(atk) && !atk.hasKeyword(Keyword.TRAMPLE)) {
|
if (combat.isBlocked(atk) && !atk.hasKeyword(Keyword.TRAMPLE)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (atk == c) {
|
if (atk == c) {
|
||||||
totalPowerUnblocked += pumpedDmg; // this accounts for Trample by now
|
totalPowerUnblocked += pumpedDmg; // this accounts for Trample by now
|
||||||
} else {
|
} else {
|
||||||
totalPowerUnblocked += ComputerUtilCombat.damageIfUnblocked(atk, opp, combat, true);
|
totalPowerUnblocked += ComputerUtilCombat.damageIfUnblocked(atk, opp, combat, true);
|
||||||
if (combat.isBlocked(atk)) {
|
if (combat.isBlocked(atk)) {
|
||||||
// consider Trample damage properly for a blocked creature
|
// consider Trample damage properly for a blocked creature
|
||||||
for (Card blk : combat.getBlockers(atk)) {
|
for (Card blk : combat.getBlockers(atk)) {
|
||||||
totalPowerUnblocked -= ComputerUtilCombat.getDamageToKill(blk, false);
|
totalPowerUnblocked -= ComputerUtilCombat.getDamageToKill(blk, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (totalPowerUnblocked >= opp.getLife()) {
|
||||||
if (totalPowerUnblocked >= opp.getLife()) {
|
return true;
|
||||||
return true;
|
} else if (totalPowerUnblocked > dmg && sa.getHostCard() != null && sa.getHostCard().isInPlay()) {
|
||||||
} else if (totalPowerUnblocked > dmg && sa.getHostCard() != null && sa.getHostCard().isInPlay()) {
|
if (sa.getPayCosts().hasNoManaCost()) {
|
||||||
if (sa.getPayCosts().hasNoManaCost()) {
|
return true; // always activate abilities which cost no mana and which can increase unblocked damage
|
||||||
return true; // always activate abilities which cost no mana and which can increase unblocked damage
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float value = 1.0f * (pumpedDmg - dmg);
|
float value = 1.0f * (pumpedDmg - dmg);
|
||||||
if (c == sa.getHostCard() && power > 0) {
|
if (c == sa.getHostCard() && power > 0) {
|
||||||
int divisor = sa.getPayCosts().getTotalMana().getCMC();
|
int divisor = sa.getPayCosts().getTotalMana().getCMC();
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import forge.ai.SpellAbilityAi;
|
|||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.effects.CharmEffect;
|
import forge.game.ability.effects.CharmEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -26,7 +27,6 @@ public class CharmAi extends SpellAbilityAi {
|
|||||||
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa);
|
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa);
|
||||||
Collections.shuffle(choices);
|
|
||||||
|
|
||||||
final int num;
|
final int num;
|
||||||
final int min;
|
final int min;
|
||||||
@@ -37,6 +37,11 @@ public class CharmAi extends SpellAbilityAi {
|
|||||||
min = sa.hasParam("MinCharmNum") ? AbilityUtils.calculateAmount(source, sa.getParam("MinCharmNum"), sa) : num;
|
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?
|
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
|
// Reset the chosen list otherwise it will be locked in forever by earlier calls
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import forge.game.player.Player;
|
|||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.player.PlayerCollection;
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.player.PlayerPredicates;
|
import forge.game.player.PlayerPredicates;
|
||||||
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
@@ -456,25 +457,24 @@ public class CountersPutAi extends CountersAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ai.getGame().getStack().isEmpty() && !SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
if (sa.usesTargeting()) {
|
||||||
// only evaluates case where all tokens are placed on a single target
|
if (!ai.getGame().getStack().isEmpty() && !SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
||||||
if (sa.usesTargeting() && sa.getMinTargets() < 2) {
|
// only evaluates case where all tokens are placed on a single target
|
||||||
if (ComputerUtilCard.canPumpAgainstRemoval(ai, sa)) {
|
if (sa.getMinTargets() < 2) {
|
||||||
Card c = sa.getTargetCard();
|
if (ComputerUtilCard.canPumpAgainstRemoval(ai, sa)) {
|
||||||
if (sa.getTargets().size() > 1) {
|
Card c = sa.getTargetCard();
|
||||||
sa.resetTargets();
|
if (sa.getTargets().size() > 1) {
|
||||||
sa.getTargets().add(c);
|
sa.resetTargets();
|
||||||
|
sa.getTargets().add(c);
|
||||||
|
}
|
||||||
|
sa.addDividedAllocation(c, amount);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
sa.addDividedAllocation(c, amount);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Targeting
|
|
||||||
if (sa.usesTargeting()) {
|
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
|
|
||||||
final boolean sacSelf = ComputerUtilCost.isSacrificeSelfCost(abCost);
|
final boolean sacSelf = ComputerUtilCost.isSacrificeSelfCost(abCost);
|
||||||
@@ -482,7 +482,7 @@ public class CountersPutAi extends CountersAi {
|
|||||||
if (sa.isCurse()) {
|
if (sa.isCurse()) {
|
||||||
list = ai.getOpponents().getCardsIn(ZoneType.Battlefield);
|
list = ai.getOpponents().getCardsIn(ZoneType.Battlefield);
|
||||||
} else {
|
} else {
|
||||||
list = new CardCollection(ai.getCardsIn(ZoneType.Battlefield));
|
list = ComputerUtil.getSafeTargets(ai, sa, ai.getCardsIn(ZoneType.Battlefield));
|
||||||
}
|
}
|
||||||
|
|
||||||
list = CardLists.filter(list, new Predicate<Card>() {
|
list = CardLists.filter(list, new Predicate<Card>() {
|
||||||
@@ -530,8 +530,7 @@ public class CountersPutAi extends CountersAi {
|
|||||||
for (int i = 1; i < amount + 1; i++) {
|
for (int i = 1; i < amount + 1; i++) {
|
||||||
int left = amount;
|
int left = amount;
|
||||||
for (Card c : list) {
|
for (Card c : list) {
|
||||||
if (ComputerUtilCard.shouldPumpCard(ai, sa, c, i, i,
|
if (ComputerUtilCard.shouldPumpCard(ai, sa, c, i, i, Lists.newArrayList())) {
|
||||||
Lists.newArrayList())) {
|
|
||||||
sa.getTargets().add(c);
|
sa.getTargets().add(c);
|
||||||
sa.addDividedAllocation(c, i);
|
sa.addDividedAllocation(c, i);
|
||||||
left -= i;
|
left -= i;
|
||||||
@@ -553,7 +552,7 @@ public class CountersPutAi extends CountersAi {
|
|||||||
// target loop
|
// target loop
|
||||||
while (sa.canAddMoreTarget()) {
|
while (sa.canAddMoreTarget()) {
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
if (!sa.isTargetNumberValid() || (sa.getTargets().size() == 0)) {
|
if (!sa.isTargetNumberValid() || sa.getTargets().isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@@ -567,34 +566,42 @@ public class CountersPutAi extends CountersAi {
|
|||||||
} else {
|
} else {
|
||||||
if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa, ai)) {
|
||||||
for (Card c : list) {
|
for (Card c : list) {
|
||||||
if (ComputerUtilCard.shouldPumpCard(ai, sa, c, amount, amount,
|
if (ComputerUtilCard.shouldPumpCard(ai, sa, c, amount, amount, Lists.newArrayList())) {
|
||||||
Lists.newArrayList())) {
|
|
||||||
choice = c;
|
|
||||||
break;
|
|
||||||
} else if (!sa.getRestrictions().isSorcerySpeed() && !ComputerUtilCard.isUselessCreature(ai, c)) {
|
|
||||||
choice = c;
|
choice = c;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!source.isSpell()) { // does not cost a card
|
|
||||||
if (choice == null) { // find generic target
|
if (choice == null) {
|
||||||
if (abCost == null
|
// try to use as cheap kill
|
||||||
|
choice = ComputerUtil.getKilledByTargeting(sa, CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (choice == null) {
|
||||||
|
// find generic target
|
||||||
|
boolean increasesCharmOutcome = false;
|
||||||
|
if (sa.getRootAbility().getApi() == ApiType.Charm && source.getStaticAbilities().isEmpty()) {
|
||||||
|
List<AbilitySub> choices = Lists.newArrayList(sa.getRootAbility().getAdditionalAbilityList("Choices"));
|
||||||
|
choices.remove(sa);
|
||||||
|
// check if other choice will already be played
|
||||||
|
increasesCharmOutcome = !choices.get(0).getTargets().isEmpty();
|
||||||
|
}
|
||||||
|
if (!source.isSpell() || increasesCharmOutcome // does not cost a card or can buff charm for no expense
|
||||||
|
|| ph.getTurn() - source.getTurnInZone() >= source.getGame().getPlayers().size() * 2) {
|
||||||
|
if (abCost == null || abCost == Cost.Zero
|
||||||
|| (ph.is(PhaseType.END_OF_TURN) && ph.getPlayerTurn().isOpponentOf(ai))) {
|
|| (ph.is(PhaseType.END_OF_TURN) && ph.getPlayerTurn().isOpponentOf(ai))) {
|
||||||
// only use at opponent EOT unless it is free
|
// only use at opponent EOT unless it is free
|
||||||
choice = chooseBoonTarget(list, type);
|
choice = chooseBoonTarget(list, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Dromoka's Command")) {
|
|
||||||
choice = chooseBoonTarget(list, type);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
choice = chooseBoonTarget(list, type);
|
choice = chooseBoonTarget(list, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (choice == null) { // can't find anything left
|
if (choice == null) { // can't find anything left
|
||||||
if (!sa.isTargetNumberValid() || sa.getTargets().size() == 0) {
|
if (!sa.isTargetNumberValid() || sa.getTargets().isEmpty()) {
|
||||||
sa.resetTargets();
|
sa.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@@ -910,7 +917,6 @@ public class CountersPutAi extends CountersAi {
|
|||||||
// Didn't want to choose anything?
|
// Didn't want to choose anything?
|
||||||
list.clear();
|
list.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ package forge.game.spellability;
|
|||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.player.Player;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@@ -51,11 +50,6 @@ public abstract class AbilityStatic extends Ability implements Cloneable {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean canPlay() {
|
public boolean canPlay() {
|
||||||
Player player = getActivatingPlayer();
|
|
||||||
if (player == null) {
|
|
||||||
player = this.getHostCard().getController();
|
|
||||||
}
|
|
||||||
|
|
||||||
final Card c = this.getHostCard();
|
final Card c = this.getHostCard();
|
||||||
|
|
||||||
return this.getRestrictions().canPlay(c, this);
|
return this.getRestrictions().canPlay(c, this);
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ ManaCost:1 B
|
|||||||
Types:Instant
|
Types:Instant
|
||||||
A:SP$ Charm | Cost$ 1 B | MinCharmNum$ 1 | CharmNum$ 2 | Choices$ DBPump,DBPutCounter
|
A:SP$ Charm | Cost$ 1 B | MinCharmNum$ 1 | CharmNum$ 2 | Choices$ DBPump,DBPutCounter
|
||||||
SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature (-1/-1) | NumAtt$ -1 | NumDef$ -1 | IsCurse$ True | SpellDescription$ Target creature gets -1/-1 until end of turn.
|
SVar:DBPump:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature (-1/-1) | NumAtt$ -1 | NumDef$ -1 | IsCurse$ True | SpellDescription$ Target creature gets -1/-1 until end of turn.
|
||||||
SVar:DBPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature (+1/+1 counter) | AILogic$ Good | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on target creature.
|
SVar:DBPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature (+1/+1 counter) | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on target creature.
|
||||||
Oracle:Choose one or both —\n• Target creature gets -1/-1 until end of turn.\n• Put a +1/+1 counter on target creature.
|
Oracle:Choose one or both —\n• Target creature gets -1/-1 until end of turn.\n• Put a +1/+1 counter on target creature.
|
||||||
|
|||||||
Reference in New Issue
Block a user