- Improve the CostDiscard logic a little for cases where CARDNAME itself is discarded.

This commit is contained in:
Michael Kamensky
2021-08-15 09:29:55 +03:00
parent 16f7ac2949
commit 43c68ddfcb
3 changed files with 26 additions and 38 deletions

View File

@@ -1,47 +1,22 @@
package forge.ai; package forge.ai;
import java.util.List;
import java.util.Set;
import forge.game.ability.ApiType;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
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 com.google.common.collect.Sets; import com.google.common.collect.Sets;
import forge.ai.AiCardMemory.MemorySet; import forge.ai.AiCardMemory.MemorySet;
import forge.ai.ability.AnimateAi; import forge.ai.ability.AnimateAi;
import forge.card.ColorSet; import forge.card.ColorSet;
import forge.game.Game; import forge.game.Game;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.ability.ApiType;
import forge.game.card.CardCollection; import forge.game.card.*;
import forge.game.card.CardCollectionView;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets; import forge.game.card.CardPredicates.Presets;
import forge.game.card.CardUtil;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.combat.Combat; import forge.game.combat.Combat;
import forge.game.cost.Cost; import forge.game.cost.*;
import forge.game.cost.CostDamage;
import forge.game.cost.CostDiscard;
import forge.game.cost.CostPart;
import forge.game.cost.CostPayLife;
import forge.game.cost.CostPayment;
import forge.game.cost.CostPutCounter;
import forge.game.cost.CostRemoveAnyCounter;
import forge.game.cost.CostRemoveCounter;
import forge.game.cost.CostSacrifice;
import forge.game.cost.CostTapType;
import forge.game.cost.PaymentDecision;
import forge.game.keyword.Keyword; import forge.game.keyword.Keyword;
import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.Spell; import forge.game.spellability.Spell;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -49,6 +24,11 @@ import forge.game.zone.ZoneType;
import forge.util.MyRandom; import forge.util.MyRandom;
import forge.util.TextUtil; import forge.util.TextUtil;
import forge.util.collect.FCollectionView; import forge.util.collect.FCollectionView;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Set;
public class ComputerUtilCost { public class ComputerUtilCost {
@@ -154,8 +134,14 @@ public class ComputerUtilCost {
final CostDiscard disc = (CostDiscard) part; final CostDiscard disc = (CostDiscard) part;
final String type = disc.getType(); final String type = disc.getType();
if (type.equals("CARDNAME") && source.getAbilityText().contains("Bloodrush")) { if (type.equals("CARDNAME")) {
continue; if (source.getAbilityText().contains("Bloodrush")) {
continue;
} else if (ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN, ai)
&& ai.getCardsIn(ZoneType.Hand).size() > ai.getMaxHandSize()) {
// Better do something than just discard stuff
return true;
}
} }
final CardCollection typeList = CardLists.getValidCards(hand, type.split(","), source.getController(), source, sa); final CardCollection typeList = CardLists.getValidCards(hand, type.split(","), source.getController(), source, sa);
if (typeList.size() > ai.getMaxHandSize()) { if (typeList.size() > ai.getMaxHandSize()) {

View File

@@ -1,12 +1,7 @@
package forge.ai; package forge.ai;
import java.util.Collection;
import java.util.List;
import java.util.Map;
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.card.CardStateName; import forge.card.CardStateName;
import forge.card.ICardFace; import forge.card.ICardFace;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
@@ -24,8 +19,13 @@ import forge.game.player.PlayerController.BinaryChoiceType;
import forge.game.spellability.AbilitySub; import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityCondition; import forge.game.spellability.SpellAbilityCondition;
import forge.game.zone.ZoneType;
import forge.util.MyRandom; import forge.util.MyRandom;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/** /**
* Base class for API-specific AI logic * Base class for API-specific AI logic
* <p> * <p>
@@ -72,10 +72,12 @@ public abstract class SpellAbilityAi {
if (sa.hasParam("AILogic")) { if (sa.hasParam("AILogic")) {
final String logic = sa.getParam("AILogic"); final String logic = sa.getParam("AILogic");
final boolean alwaysOnDiscard = "AlwaysOnDiscard".equals(logic) && ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN, ai)
&& ai.getCardsIn(ZoneType.Hand).size() > ai.getMaxHandSize();
if (!checkAiLogic(ai, sa, logic)) { if (!checkAiLogic(ai, sa, logic)) {
return false; return false;
} }
if (!checkPhaseRestrictions(ai, sa, ai.getGame().getPhaseHandler(), logic)) { if (!alwaysOnDiscard && !checkPhaseRestrictions(ai, sa, ai.getGame().getPhaseHandler(), logic)) {
return false; return false;
} }
} else { } else {

View File

@@ -5,6 +5,6 @@ A:SP$ DealDamage | Cost$ 6 U R | ValidTgts$ Creature,Player,Planeswalker | TgtPr
SVar:DBTap:DB$ Tap | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Permanent | TgtPrompt$ Select target permanent to tap | SubAbility$ DBToken SVar:DBTap:DB$ Tap | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Permanent | TgtPrompt$ Select target permanent to tap | SubAbility$ DBToken
SVar:DBToken:DB$ Token | TokenScript$ ur_4_4_elemental | SubAbility$ DBDraw SVar:DBToken:DB$ Token | TokenScript$ ur_4_4_elemental | SubAbility$ DBDraw
SVar:DBDraw:DB$ Draw | NumCards$ 2 SVar:DBDraw:DB$ Draw | NumCards$ 2
A:AB$ Token | Cost$ UR UR Discard<1/CARDNAME> | ActivationZone$ Hand | TokenScript$ c_a_treasure_sac | SpellDescription$ Create a Treasure token. A:AB$ Token | Cost$ UR UR Discard<1/CARDNAME> | ActivationZone$ Hand | TokenScript$ c_a_treasure_sac | AILogic$ AlwaysOnDiscard | SpellDescription$ Create a Treasure token.
DeckHas:Ability$Token DeckHas:Ability$Token
Oracle:Magma Opus deals 4 damage divided as you choose among any number of targets. Tap two target permanents. Create a 4/4 blue and red Elemental creature token. Draw two cards.\n{U/R}{U/R}, Discard Magma Opus: Create a Treasure token. Oracle:Magma Opus deals 4 damage divided as you choose among any number of targets. Tap two target permanents. Create a 4/4 blue and red Elemental creature token. Draw two cards.\n{U/R}{U/R}, Discard Magma Opus: Create a Treasure token.