mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +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;
|
||||
|
||||
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.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.ai.ability.AnimateAi;
|
||||
import forge.card.CardTypeView;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.ability.effects.ProtectEffect;
|
||||
import forge.game.card.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.card.*;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.combat.GlobalAttackRestrictions;
|
||||
@@ -53,6 +37,8 @@ import forge.game.player.Player;
|
||||
import forge.game.player.PlayerCollection;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityPredicates;
|
||||
import forge.game.staticability.StaticAbility;
|
||||
import forge.game.staticability.StaticAbilityAssignCombatDamageAsUnblocked;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.Zone;
|
||||
@@ -62,6 +48,12 @@ import forge.util.Expressions;
|
||||
import forge.util.MyRandom;
|
||||
import forge.util.collect.FCollection;
|
||||
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) {
|
||||
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>() {
|
||||
@Override
|
||||
public boolean apply(Card c) {
|
||||
@@ -142,14 +136,33 @@ public class AiAttackController {
|
||||
}
|
||||
};
|
||||
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;
|
||||
}
|
||||
*/
|
||||
for (SpellAbility sa : Iterables.filter(c.getSpellAbilities(), SpellAbilityPredicates.isApi(ApiType.Animate))) {
|
||||
if (ComputerUtilCost.canPayCost(sa, defender, false)
|
||||
&& sa.getRestrictions().checkOtherRestrictions(c, sa, defender)) {
|
||||
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
|
||||
* @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()) {
|
||||
for (SpellAbility sa : original.getSpellAbilities()) {
|
||||
if (sa.getApi() == ApiType.SetState && ComputerUtilCost.canPayCost(sa, original.getController(), false)) {
|
||||
|
||||
Reference in New Issue
Block a user