mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38: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 damage = attacker.getNetCombatDamage();
|
||||||
int poison = 0;
|
int poison = 0;
|
||||||
damage += predictPowerBonusOfAttacker(attacker, null, null, false);
|
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);
|
int pd = predictDamageTo(attacked, damage, attacker, true);
|
||||||
// opponent can always order it so that he gets 0
|
// 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"))) {
|
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;
|
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) {
|
public static Card applyPotentialAttackCloneTriggers(Card attacker) {
|
||||||
// This method returns the potentially cloned card if the creature turns into something else during the attack
|
// 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
|
// (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.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
import forge.game.card.CardPredicates.Presets;
|
import forge.game.card.CardPredicates.Presets;
|
||||||
|
import forge.game.combat.Combat;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
import forge.game.cost.CostTap;
|
import forge.game.cost.CostTap;
|
||||||
import forge.game.mana.ManaCostBeingPaid;
|
import forge.game.mana.ManaCostBeingPaid;
|
||||||
@@ -37,6 +38,10 @@ public class UntapAi extends SpellAbilityAi {
|
|||||||
return false;
|
return false;
|
||||||
} else if ("PoolExtraMana".equals(aiLogic)) {
|
} else if ("PoolExtraMana".equals(aiLogic)) {
|
||||||
return doPoolExtraManaLogic(ai, sa);
|
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));
|
return !("Never".equals(aiLogic));
|
||||||
@@ -63,6 +68,8 @@ public class UntapAi extends SpellAbilityAi {
|
|||||||
final List<Card> pDefined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
final List<Card> pDefined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
|
||||||
return pDefined.isEmpty() || (pDefined.get(0).isTapped() && pDefined.get(0).getController() == ai);
|
return pDefined.isEmpty() || (pDefined.get(0).isTapped() && pDefined.get(0).getController() == ai);
|
||||||
} else {
|
} else {
|
||||||
|
// If we already selected a target just use that
|
||||||
|
|
||||||
return untapPrefTargeting(ai, sa, false);
|
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) {
|
private static boolean untapPrefTargeting(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||||
final Card source = sa.getHostCard();
|
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();
|
sa.resetTargets();
|
||||||
|
|
||||||
final PlayerCollection targetController = new PlayerCollection();
|
final PlayerCollection targetController = new PlayerCollection();
|
||||||
@@ -337,6 +351,50 @@ public class UntapAi extends SpellAbilityAi {
|
|||||||
return null;
|
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) {
|
private boolean doPoolExtraManaLogic(final Player ai, final SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
final PhaseHandler ph = source.getGame().getPhaseHandler();
|
final PhaseHandler ph = source.getGame().getPhaseHandler();
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
Name:Maze of Ith
|
Name:Maze of Ith
|
||||||
ManaCost:no cost
|
ManaCost:no cost
|
||||||
Types:Land
|
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: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: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
|
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.
|
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
|
ManaCost:no cost
|
||||||
Types:Land
|
Types:Land
|
||||||
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
|
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: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: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
|
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.
|
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}.
|
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}."
|
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: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}."
|
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