From 9e9a5aea86dc0322e478d6050535e5ecedb4aea6 Mon Sep 17 00:00:00 2001 From: Sloth Date: Sun, 19 Jul 2015 11:38:59 +0000 Subject: [PATCH] - The AI will no longer kill itself with damage from Eidolon of the Great Revel, Ruric Thar, the Unbowed, Spellshock or similar cards. --- .../src/main/java/forge/ai/AiController.java | 16 ++++- .../src/main/java/forge/ai/ComputerUtil.java | 70 ++++++++++++++++++- 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 8230b841ad2..4ffe2fb68c0 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -711,6 +711,11 @@ public class AiController { return canPlayFromEffectAI((SpellPermanent)sa, false, true); } if (sa instanceof Spell) { + + if (ComputerUtil.getDamageForPlaying(player, sa) >= player.getLife() + && !player.cantLoseForZeroOrLessLife() && player.canLoseLife()) { + return AiPlayDecision.CurseEffects; + } return canPlaySpellBasic(card); } return AiPlayDecision.WillPlay; @@ -1009,6 +1014,13 @@ public class AiController { public AiPlayDecision canPlayFromEffectAI(Spell spell, boolean mandatory, boolean withoutPayingManaCost) { final Card card = spell.getHostCard(); + + int damage = ComputerUtil.getDamageForPlaying(player, spell); + + if (damage >= player.getLife() && !player.cantLoseForZeroOrLessLife() && player.canLoseLife()) { + return AiPlayDecision.CurseEffects; + } + if (spell instanceof SpellApiBased) { boolean chance = false; if (withoutPayingManaCost) { @@ -1081,7 +1093,7 @@ public class AiController { if (!checkETBEffects(card, spell, null)) { return AiPlayDecision.BadEtbEffects; } - if (ComputerUtil.damageFromETB(player, card) >= player.getLife() && !player.cantLoseForZeroOrLessLife() + if (damage + ComputerUtil.getDamageFromETB(player, card) >= player.getLife() && !player.cantLoseForZeroOrLessLife() && player.canLoseLife()) { return AiPlayDecision.BadEtbEffects; } @@ -1097,7 +1109,7 @@ public class AiController { CardCollection landsWannaPlay = getLandsToPlay(); if (landsWannaPlay != null && !landsWannaPlay.isEmpty() && player.canPlayLand(null)) { Card land = chooseBestLandToPlay(landsWannaPlay); - if (ComputerUtil.damageFromETB(player, land) < player.getLife() || !player.canLoseLife() + if (ComputerUtil.getDamageFromETB(player, land) < player.getLife() || !player.canLoseLife() || player.cantLoseForZeroOrLessLife() ) { game.PLAY_LAND_SURROGATE.setHostCard(land); final List abilities = new ArrayList(); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 13ec85d6b00..fd1e8377ce5 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -1766,8 +1766,76 @@ public class ComputerUtil { }); return ComputerUtilCard.getBestCreatureAI(killables); } + + public static int getDamageForPlaying(final Player player, final SpellAbility sa) { + + // check for bad spell cast triggers + int damage = 0; + final Game game = player.getGame(); + final Card card = sa.getHostCard(); + final FCollection theTriggers = new FCollection(); - public static int damageFromETB(final Player player, final Card permanent) { + for (Card c : game.getCardsIn(ZoneType.Battlefield)) { + theTriggers.addAll(c.getTriggers()); + } + for (Trigger trigger : theTriggers) { + Map trigParams = trigger.getMapParams(); + final Card source = trigger.getHostCard(); + + + if (!trigger.zonesCheck(game.getZoneOf(source))) { + continue; + } + if (!trigger.requirementsCheck(game)) { + continue; + } + TriggerType mode = trigger.getMode(); + if (mode != TriggerType.SpellCast) { + continue; + } + if (trigParams.containsKey("ValidCard")) { + if (!card.isValid(trigParams.get("ValidCard"), source.getController(), source)) { + continue; + } + } + + if (trigParams.containsKey("ValidActivatingPlayer")) { + if (!player.isValid(trigParams.get("ValidActivatingPlayer"), source.getController(), source)) { + continue; + } + } + + String ability = source.getSVar(trigParams.get("Execute")); + if (ability.isEmpty()) { + continue; + } + + final Map abilityParams = AbilityFactory.getMapParams(ability); + if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("DealDamage")) + || (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("DealDamage"))) { + if (!"TriggeredActivator".equals(abilityParams.get("Defined"))) { + continue; + } + if (!abilityParams.containsKey("NumDmg")) { + continue; + } + damage += ComputerUtilCombat.predictDamageTo(player, AbilityUtils.calculateAmount(source, abilityParams.get("NumDmg"), null), source, false); + } else if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("LoseLife")) + || (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("LoseLife"))) { + if (!"TriggeredActivator".equals(abilityParams.get("Defined"))) { + continue; + } + if (!abilityParams.containsKey("LifeAmount")) { + continue; + } + damage += AbilityUtils.calculateAmount(source, abilityParams.get("LifeAmount"), null); + } + } + + return damage; + } + + public static int getDamageFromETB(final Player player, final Card permanent) { int damage = 0; final Game game = player.getGame(); final FCollection theTriggers = new FCollection();