mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Add ai usage for Maze of Ith
This commit is contained in:
@@ -223,7 +223,7 @@ public class ComputerUtilCombat {
|
||||
int damage = attacker.getNetCombatDamage();
|
||||
int poison = 0;
|
||||
damage += predictPowerBonusOfAttacker(attacker, null, null, false);
|
||||
if (attacker.hasKeyword(Keyword.INFECT)) {
|
||||
if (attacker.hasKeyword(Keyword.INFECT) || attacker.hasKeyword(Keyword.TOXIC)) {
|
||||
int pd = predictDamageTo(attacked, damage, attacker, true);
|
||||
// opponent can always order it so that he gets 0
|
||||
if (pd == 1 && Iterables.any(attacker.getController().getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Vorinclex, Monstrous Raider"))) {
|
||||
@@ -2390,6 +2390,62 @@ public class ComputerUtilCombat {
|
||||
return categorizedAttackers;
|
||||
}
|
||||
|
||||
public static Card mostDangerousAttacker(CardCollection list, Player ai, Combat combat, boolean withAbilities) {
|
||||
Card damageCard = null;
|
||||
Card poisonCard = null;
|
||||
|
||||
int damageScore = 0;
|
||||
int poisonScore = 0;
|
||||
|
||||
|
||||
for(Card c : list) {
|
||||
int estimatedDmg = damageIfUnblocked(c, ai, combat, withAbilities);
|
||||
int estimatedPoison = poisonIfUnblocked(c, ai);
|
||||
|
||||
if (combat.isBlocked(c)) {
|
||||
if (!c.hasKeyword(Keyword.TRAMPLE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int absorbedByToughness = 0;
|
||||
for (Card blocker : combat.getBlockers(c)) {
|
||||
absorbedByToughness += blocker.getNetToughness();
|
||||
}
|
||||
estimatedPoison -= absorbedByToughness;
|
||||
estimatedDmg -= absorbedByToughness;
|
||||
}
|
||||
|
||||
if (estimatedDmg > damageScore) {
|
||||
damageScore = estimatedDmg;
|
||||
damageCard = c;
|
||||
}
|
||||
|
||||
if (estimatedPoison > poisonScore) {
|
||||
poisonScore = estimatedPoison;
|
||||
poisonCard = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (damageCard == null && poisonCard == null) {
|
||||
return null;
|
||||
} else if (damageCard == null) {
|
||||
return poisonCard;
|
||||
} else if (poisonCard == null) {
|
||||
return damageCard;
|
||||
}
|
||||
|
||||
int life = ai.getLife();
|
||||
int poisonLife = 10 - ai.getPoisonCounters();
|
||||
double percentLife = life * 1.0 / damageScore;
|
||||
double percentPoison = poisonLife * 1.0 / poisonScore;
|
||||
|
||||
if (percentLife >= percentPoison) {
|
||||
return damageCard;
|
||||
} else {
|
||||
return poisonCard;
|
||||
}
|
||||
}
|
||||
|
||||
public static Card applyPotentialAttackCloneTriggers(Card attacker) {
|
||||
// This method returns the potentially cloned card if the creature turns into something else during the attack
|
||||
// (currently looks for the creature with maximum raw power since that's what the AI usually judges by when
|
||||
|
||||
@@ -13,6 +13,7 @@ import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostTap;
|
||||
import forge.game.mana.ManaCostBeingPaid;
|
||||
@@ -37,6 +38,10 @@ public class UntapAi extends SpellAbilityAi {
|
||||
return false;
|
||||
} else if ("PoolExtraMana".equals(aiLogic)) {
|
||||
return doPoolExtraManaLogic(ai, sa);
|
||||
} else if ("PreventCombatDamage".equals(aiLogic)) {
|
||||
return doPreventCombatDamageLogic(ai, sa);
|
||||
// In the future if you want to give Pseudo vigilance to a creature you attacked with
|
||||
// activate during your own during the end of combat step
|
||||
}
|
||||
|
||||
return !("Never".equals(aiLogic));
|
||||
@@ -63,6 +68,8 @@ public class UntapAi extends SpellAbilityAi {
|
||||
final List<Card> pDefined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
||||
return pDefined.isEmpty() || (pDefined.get(0).isTapped() && pDefined.get(0).getController() == ai);
|
||||
} else {
|
||||
// If we already selected a target just use that
|
||||
|
||||
return untapPrefTargeting(ai, sa, false);
|
||||
}
|
||||
}
|
||||
@@ -118,6 +125,13 @@ public class UntapAi extends SpellAbilityAi {
|
||||
*/
|
||||
private static boolean untapPrefTargeting(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||
final Card source = sa.getHostCard();
|
||||
|
||||
if (alreadyAssignedTarget(sa)) {
|
||||
if (sa.getTargets().size() > 0) {
|
||||
// If we selected something lets assume its valid
|
||||
return true;
|
||||
}
|
||||
}
|
||||
sa.resetTargets();
|
||||
|
||||
final PlayerCollection targetController = new PlayerCollection();
|
||||
@@ -337,6 +351,50 @@ public class UntapAi extends SpellAbilityAi {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean doPreventCombatDamageLogic(final Player ai, final SpellAbility sa) {
|
||||
// Only Maze of Ith and Maze of Shadows uses this. Feel free to use it aggressively.
|
||||
Game game = ai.getGame();
|
||||
Card source = sa.getHostCard();
|
||||
sa.resetTargets();
|
||||
|
||||
if (!game.getPhaseHandler().getPlayerTurn().isOpponentOf(ai)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If damage can't be prevented. Just return false.
|
||||
|
||||
Combat activeCombat = game.getCombat();
|
||||
if (activeCombat == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CardCollection list = CardLists.getTargetableCards(activeCombat.getAttackers(), sa);
|
||||
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||
// Blockers already set. Are there any dangerous unblocked creatures? Sort by creature that will deal the most damage?
|
||||
Card card = ComputerUtilCombat.mostDangerousAttacker(list, ai, activeCombat, true);
|
||||
|
||||
if (card == null) { return false; }
|
||||
|
||||
sa.getTargets().add(card);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean alreadyAssignedTarget(final SpellAbility sa) {
|
||||
if (sa.hasParam("AILogic")) {
|
||||
String aiLogic = sa.getParam("AILogic");
|
||||
return "PreventCombatDamage".equals(aiLogic);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean doPoolExtraManaLogic(final Player ai, final SpellAbility sa) {
|
||||
final Card source = sa.getHostCard();
|
||||
final PhaseHandler ph = source.getGame().getPhaseHandler();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Name:Maze of Ith
|
||||
ManaCost:no cost
|
||||
Types:Land
|
||||
A:AB$ Untap | Cost$ T | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature | SubAbility$ DBPump | SpellDescription$ Untap target attacking creature.
|
||||
A:AB$ Untap | Cost$ T | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature | AILogic$ PreventCombatDamage | SubAbility$ DBPump | SpellDescription$ Untap target attacking creature.
|
||||
SVar:DBPump:DB$ Effect | ReplacementEffects$ RPrevent1,RPrevent2 | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | SpellDescription$ Prevent all combat damage that would be dealt to and dealt by that creature this turn.
|
||||
SVar:RPrevent1:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidSource$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn.
|
||||
SVar:RPrevent2:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn. | Secondary$ True
|
||||
AI:RemoveDeck:All
|
||||
AI:RemoveDeck:Random
|
||||
Oracle:{T}: Untap target attacking creature. Prevent all combat damage that would be dealt to and dealt by that creature this turn.
|
||||
|
||||
@@ -2,9 +2,9 @@ Name:Maze of Shadows
|
||||
ManaCost:no cost
|
||||
Types:Land
|
||||
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
|
||||
A:AB$ Untap | Cost$ T | ValidTgts$ Creature.attacking+withShadow | TgtPrompt$ Select target attacking creature with shadow | SubAbility$ DBPump | SpellDescription$ Untap target attacking creature with shadow.
|
||||
A:AB$ Untap | Cost$ T | ValidTgts$ Creature.attacking+withShadow | TgtPrompt$ Select target attacking creature with shadow | AILogic$ PreventCombatDamage | SubAbility$ DBPump | SpellDescription$ Untap target attacking creature with shadow.
|
||||
SVar:DBPump:DB$ Effect | ReplacementEffects$ RPrevent1,RPrevent2 | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | SpellDescription$ Prevent all combat damage that would be dealt to and dealt by that creature this turn.
|
||||
SVar:RPrevent1:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidSource$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn.
|
||||
SVar:RPrevent2:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn. | Secondary$ True
|
||||
AI:RemoveDeck:All
|
||||
AI:RemoveDeck:Random
|
||||
Oracle:{T}: Add {C}.\n{T}: Untap target attacking creature with shadow. Prevent all combat damage that would be dealt to and dealt by that creature this turn.
|
||||
|
||||
@@ -4,4 +4,6 @@ Types:Land
|
||||
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
|
||||
S:Mode$ Continuous | EffectZone$ Graveyard | Affected$ Land.YouCtrl | AddAbility$ AddMana | Description$ As long as Riftstone Portal is in your graveyard, lands you control have "{T}: Add {G} or {W}."
|
||||
SVar:AddMana:AB$ Mana | Cost$ T | Produced$ Combo G W | SpellDescription$ Add {G} or {W}.
|
||||
SVar:SacMe:2
|
||||
SVar:DiscardMe:1
|
||||
Oracle:{T}: Add {C}.\nAs long as Riftstone Portal is in your graveyard, lands you control have "{T}: Add {G} or {W}."
|
||||
|
||||
Reference in New Issue
Block a user