mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
AI: Account for transformable cards (e.g. Incubator tokens) when considering defenders (#3232)
* - AI: Account for transformable cards (e.g. Incubator tokens) when looking for possible defenders. * - Check if the transformed card is actually a creature * - Some recommended improvements to the logic - Comment out a block of code which might not be as useful under the current rules and with contemporary cards anymore * - Style fix * - Attempt to count the total mana spent on transforming stuff when determining how many extra defenders are available. * - Check pay costs for Animate as well as for Transform. - NPE guard for pay costs check.
This commit is contained in:
@@ -17,33 +17,17 @@
|
|||||||
*/
|
*/
|
||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import forge.game.staticability.StaticAbility;
|
|
||||||
import forge.game.staticability.StaticAbilityAssignCombatDamageAsUnblocked;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import forge.ai.ability.AnimateAi;
|
import forge.ai.ability.AnimateAi;
|
||||||
import forge.card.CardTypeView;
|
import forge.card.CardTypeView;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.ApiType;
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.ability.effects.ProtectEffect;
|
import forge.game.ability.effects.ProtectEffect;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.*;
|
||||||
import forge.game.card.CardCollection;
|
|
||||||
import forge.game.card.CardCollectionView;
|
|
||||||
import forge.game.card.CardLists;
|
|
||||||
import forge.game.card.CardPredicates;
|
|
||||||
import forge.game.card.CardUtil;
|
|
||||||
import forge.game.card.CounterEnumType;
|
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.combat.CombatUtil;
|
import forge.game.combat.CombatUtil;
|
||||||
import forge.game.combat.GlobalAttackRestrictions;
|
import forge.game.combat.GlobalAttackRestrictions;
|
||||||
@@ -53,6 +37,8 @@ import forge.game.player.Player;
|
|||||||
import forge.game.player.PlayerCollection;
|
import forge.game.player.PlayerCollection;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityPredicates;
|
import forge.game.spellability.SpellAbilityPredicates;
|
||||||
|
import forge.game.staticability.StaticAbility;
|
||||||
|
import forge.game.staticability.StaticAbilityAssignCombatDamageAsUnblocked;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.game.zone.Zone;
|
import forge.game.zone.Zone;
|
||||||
@@ -62,6 +48,12 @@ import forge.util.Expressions;
|
|||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
import forge.util.collect.FCollection;
|
import forge.util.collect.FCollection;
|
||||||
import forge.util.collect.FCollectionView;
|
import forge.util.collect.FCollectionView;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -135,6 +127,8 @@ public class AiAttackController {
|
|||||||
|
|
||||||
public static List<Card> getOpponentCreatures(final Player defender) {
|
public static List<Card> getOpponentCreatures(final Player defender) {
|
||||||
List<Card> defenders = defender.getCreaturesInPlay();
|
List<Card> defenders = defender.getCreaturesInPlay();
|
||||||
|
int totalMana = ComputerUtilMana.getAvailableManaEstimate(defender, true);
|
||||||
|
int manaReserved = 0; // for paying the cost to transform
|
||||||
Predicate<Card> canAnimate = new Predicate<Card>() {
|
Predicate<Card> canAnimate = new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Card c) {
|
public boolean apply(Card c) {
|
||||||
@@ -142,14 +136,33 @@ public class AiAttackController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
for (Card c : CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), canAnimate)) {
|
for (Card c : CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), canAnimate)) {
|
||||||
if (c.isToken() && c.getCopiedPermanent() == null) {
|
/* TODO: is the commented out code still necessary for anything?
|
||||||
|
if (c.isToken() && c.getCopiedPermanent() == null && !c.canTransform(null)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
for (SpellAbility sa : Iterables.filter(c.getSpellAbilities(), SpellAbilityPredicates.isApi(ApiType.Animate))) {
|
for (SpellAbility sa : Iterables.filter(c.getSpellAbilities(), SpellAbilityPredicates.isApi(ApiType.Animate))) {
|
||||||
if (ComputerUtilCost.canPayCost(sa, defender, false)
|
if (ComputerUtilCost.canPayCost(sa, defender, false)
|
||||||
&& sa.getRestrictions().checkOtherRestrictions(c, sa, defender)) {
|
&& sa.getRestrictions().checkOtherRestrictions(c, sa, defender)) {
|
||||||
Card animatedCopy = AnimateAi.becomeAnimated(c, sa);
|
Card animatedCopy = AnimateAi.becomeAnimated(c, sa);
|
||||||
defenders.add(animatedCopy);
|
int saCMC = sa.getPayCosts() != null && sa.getPayCosts().hasManaCost() ?
|
||||||
|
sa.getPayCosts().getTotalMana().getCMC() : 0; // FIXME: imprecise, only works 100% for colorless mana
|
||||||
|
if (totalMana - manaReserved >= saCMC) {
|
||||||
|
manaReserved += saCMC;
|
||||||
|
defenders.add(animatedCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Transform (e.g. Incubator tokens)
|
||||||
|
for (SpellAbility sa : Iterables.filter(c.getSpellAbilities(), SpellAbilityPredicates.isApi(ApiType.SetState))) {
|
||||||
|
Card transformedCopy = ComputerUtilCombat.canTransform(c);
|
||||||
|
if (transformedCopy.isCreature()) {
|
||||||
|
int saCMC = sa.getPayCosts() != null && sa.getPayCosts().hasManaCost() ?
|
||||||
|
sa.getPayCosts().getTotalMana().getCMC() : 0; // FIXME: imprecise, only works 100% for colorless mana
|
||||||
|
if (totalMana - manaReserved >= saCMC) {
|
||||||
|
manaReserved += saCMC;
|
||||||
|
defenders.add(transformedCopy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2307,7 +2307,7 @@ public class ComputerUtilCombat {
|
|||||||
* @param original original creature
|
* @param original original creature
|
||||||
* @return transform creature if possible, original creature otherwise
|
* @return transform creature if possible, original creature otherwise
|
||||||
*/
|
*/
|
||||||
private final static Card canTransform(Card original) {
|
public final static Card canTransform(Card original) {
|
||||||
if (original.isTransformable() && !original.isInAlternateState()) {
|
if (original.isTransformable() && !original.isInAlternateState()) {
|
||||||
for (SpellAbility sa : original.getSpellAbilities()) {
|
for (SpellAbility sa : original.getSpellAbilities()) {
|
||||||
if (sa.getApi() == ApiType.SetState && ComputerUtilCost.canPayCost(sa, original.getController(), false)) {
|
if (sa.getApi() == ApiType.SetState && ComputerUtilCost.canPayCost(sa, original.getController(), false)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user