- Improve AI logic for Tetzimoc, Primal Death; ensure that the AI actually plays it (used not to play at all).

This commit is contained in:
Agetian
2018-05-08 20:11:47 +03:00
parent 5d43733add
commit 82afad3b67
3 changed files with 35 additions and 12 deletions

View File

@@ -1,6 +1,7 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
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.*; import forge.ai.*;
@@ -125,6 +126,8 @@ public class CountersPutAi extends SpellAbilityAi {
final String type = sa.getParam("CounterType"); final String type = sa.getParam("CounterType");
final String amountStr = sa.getParam("CounterNum"); final String amountStr = sa.getParam("CounterNum");
final boolean divided = sa.hasParam("DividedAsYouChoose"); final boolean divided = sa.hasParam("DividedAsYouChoose");
final String logic = sa.getParamOrDefault("AILogic", "");
PhaseHandler ph = ai.getGame().getPhaseHandler();
final boolean isClockwork = "True".equals(sa.getParam("UpTo")) && "Self".equals(sa.getParam("Defined")) final boolean isClockwork = "True".equals(sa.getParam("UpTo")) && "Self".equals(sa.getParam("Defined"))
&& "P1P0".equals(sa.getParam("CounterType")) && "Count$xPaid".equals(source.getSVar("X")) && "P1P0".equals(sa.getParam("CounterType")) && "Count$xPaid".equals(source.getSVar("X"))
@@ -214,15 +217,15 @@ public class CountersPutAi extends SpellAbilityAi {
return false; return false;
} }
if ("Never".equals(sa.getParam("AILogic"))) { if ("Never".equals(logic)) {
return false; return false;
} }
if ("PayEnergy".equals(sa.getParam("AILogic"))) { if ("PayEnergy".equals(logic)) {
return true; return true;
} }
if ("PayEnergyConservatively".equals(sa.getParam("AILogic"))) { if ("PayEnergyConservatively".equals(logic)) {
boolean onlyInCombat = ai.getController().isAI() boolean onlyInCombat = ai.getController().isAI()
&& ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT); && ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT);
boolean onlyDefensive = ai.getController().isAI() boolean onlyDefensive = ai.getController().isAI()
@@ -264,6 +267,22 @@ public class CountersPutAi extends SpellAbilityAi {
} }
} }
if (logic.equals("MarkOppCreature")) {
if (!ph.is(PhaseType.END_OF_TURN)) {
return false;
}
CardCollection oppCreats = CardLists.filter(ai.getOpponents().getCreaturesInPlay(),
Predicates.not(CardPredicates.hasCounter(CounterType.getType(type))));
if (!oppCreats.isEmpty()) {
Card bestCreat = ComputerUtilCard.getBestCreatureAI(oppCreats);
sa.resetTargets();
sa.getTargets().add(bestCreat);
return true;
}
}
if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) { if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) {
return false; return false;
} }
@@ -292,7 +311,7 @@ public class CountersPutAi extends SpellAbilityAi {
// TODO handle proper calculation of X values based on Cost // TODO handle proper calculation of X values based on Cost
int amount = AbilityUtils.calculateAmount(source, amountStr, sa); int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
if ("Fight".equals(sa.getParam("AILogic"))) { if ("Fight".equals(logic)) {
int nPump = 0; int nPump = 0;
if (type.equals("P1P1")) { if (type.equals("P1P1")) {
nPump = amount; nPump = amount;
@@ -323,7 +342,7 @@ public class CountersPutAi extends SpellAbilityAi {
} }
source.setSVar("PayX", Integer.toString(amount)); source.setSVar("PayX", Integer.toString(amount));
} else if ("ExiledCreatureFromGraveCMC".equals(sa.getParam("AILogic"))) { } else if ("ExiledCreatureFromGraveCMC".equals(logic)) {
// e.g. Necropolis // e.g. Necropolis
amount = Aggregates.max(CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES), CardPredicates.Accessors.fnGetCmc); amount = Aggregates.max(CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES), CardPredicates.Accessors.fnGetCmc);
if (amount > 0 && ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN)) { if (amount > 0 && ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN)) {
@@ -337,7 +356,7 @@ public class CountersPutAi extends SpellAbilityAi {
return false; return false;
} }
if ("Polukranos".equals(sa.getParam("AILogic"))) { if ("Polukranos".equals(logic)) {
CardCollection humCreatures = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa); CardCollection humCreatures = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa);
@@ -360,9 +379,7 @@ public class CountersPutAi extends SpellAbilityAi {
} }
} }
PhaseHandler ph = ai.getGame().getPhaseHandler(); if ("AlwaysAtOppEOT".equals(logic)) {
if ("AlwaysAtOppEOT".equals(sa.getParam("AILogic"))) {
if (ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai)) { if (ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai)) {
return true; return true;
} }
@@ -566,6 +583,7 @@ public class CountersPutAi extends SpellAbilityAi {
final Game game = ai.getGame(); final Game game = ai.getGame();
Card choice = null; Card choice = null;
final String type = sa.getParam("CounterType"); final String type = sa.getParam("CounterType");
final String logic = sa.getParamOrDefault("AILogic", "");
final String amountStr = sa.getParam("CounterNum"); final String amountStr = sa.getParam("CounterNum");
final boolean divided = sa.hasParam("DividedAsYouChoose"); final boolean divided = sa.hasParam("DividedAsYouChoose");
@@ -611,7 +629,7 @@ public class CountersPutAi extends SpellAbilityAi {
SpellAbility animate = sa.findSubAbilityByType(ApiType.Animate); SpellAbility animate = sa.findSubAbilityByType(ApiType.Animate);
if (!lands.isEmpty() && animate != null) { if (!lands.isEmpty() && animate != null) {
choice = ComputerUtilCard.getWorstLand(lands); choice = ComputerUtilCard.getWorstLand(lands);
} else if ("BoonCounterOnOppCreature".equals(sa.getParam("AILogic"))) { } else if ("BoonCounterOnOppCreature".equals(logic)) {
choice = ComputerUtilCard.getWorstCreatureAI(list); choice = ComputerUtilCard.getWorstCreatureAI(list);
} else { } else {
choice = CountersAi.chooseBoonTarget(list, type); choice = CountersAi.chooseBoonTarget(list, type);

View File

@@ -64,10 +64,15 @@ public class DestroyAllAi extends SpellAbilityAi {
public boolean doMassRemovalLogic(Player ai, SpellAbility sa) { public boolean doMassRemovalLogic(Player ai, SpellAbility sa) {
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final String logic = sa.getParamOrDefault("AILogic", "");
Player opponent = ComputerUtil.getOpponentFor(ai); // TODO: how should this AI logic work for multiplayer and getOpponents()? Player opponent = ComputerUtil.getOpponentFor(ai); // TODO: how should this AI logic work for multiplayer and getOpponents()?
final int CREATURE_EVAL_THRESHOLD = 200; final int CREATURE_EVAL_THRESHOLD = 200;
if (logic.equals("Always")) {
return true; // e.g. Tetzimoc, Primal Death, where we want to cast the permanent even if the removal trigger does nothing
}
String valid = ""; String valid = "";
if (sa.hasParam("ValidCards")) { if (sa.hasParam("ValidCards")) {
valid = sa.getParam("ValidCards"); valid = sa.getParam("ValidCards");

View File

@@ -3,8 +3,8 @@ ManaCost:4 B B
Types:Legendary Creature Elder Dinosaur Types:Legendary Creature Elder Dinosaur
PT:6/6 PT:6/6
K:Deathtouch K:Deathtouch
A:AB$ PutCounter | Cost$ B | ActivationZone$ Hand | CostDesc$ {B}, Reveal CARDNAME from your hand: | PlayerTurn$ True | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ PREY | CounterNum$ 1 | IsCurse$ True | SpellDescription$ Put a prey counter on target creature. Activate this ability only during your turn. A:AB$ PutCounter | Cost$ B | ActivationZone$ Hand | CostDesc$ {B}, Reveal CARDNAME from your hand: | PlayerTurn$ True | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ PREY | CounterNum$ 1 | IsCurse$ True | AILogic$ MarkOppCreature | SpellDescription$ Put a prey counter on target creature. Activate this ability only during your turn.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDestroyAll | TriggerDescription$ When CARDNAME enters the battlefield, destroy each creature your opponents control with a prey counter on it. T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDestroyAll | TriggerDescription$ When CARDNAME enters the battlefield, destroy each creature your opponents control with a prey counter on it.
SVar:TrigDestroyAll:DB$ DestroyAll | ValidCards$ Creature.OppCtrl+counters_GE1_PREY SVar:TrigDestroyAll:DB$ DestroyAll | ValidCards$ Creature.OppCtrl+counters_GE1_PREY | AILogic$ Always
SVar:Picture:http://www.wizards.com/global/images/magic/general/tetzimoc_primal_death.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/tetzimoc_primal_death.jpg
Oracle:Deathtouch\n{B}, Reveal Tetzimoc, Primal Death from your hand: Put a prey counter on target creature. Activate this ability only during your turn.\nWhen Tetzimoc enters the battlefield, destroy each creature your opponents control with a prey counter on it. Oracle:Deathtouch\n{B}, Reveal Tetzimoc, Primal Death from your hand: Put a prey counter on target creature. Activate this ability only during your turn.\nWhen Tetzimoc enters the battlefield, destroy each creature your opponents control with a prey counter on it.