diff --git a/.gitattributes b/.gitattributes index 36cdf887b01..af5c50b2df9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3495,6 +3495,7 @@ forge-gui/res/cardsfolder/d/diamond_faerie.txt svneol=native#text/plain forge-gui/res/cardsfolder/d/diamond_faerie_avatar.txt -text forge-gui/res/cardsfolder/d/diamond_kaleidoscope.txt svneol=native#text/plain forge-gui/res/cardsfolder/d/diamond_valley.txt svneol=native#text/plain +forge-gui/res/cardsfolder/d/diaochan_artful_beauty.txt -text forge-gui/res/cardsfolder/d/dichotomancy.txt -text forge-gui/res/cardsfolder/d/didgeridoo.txt svneol=native#text/plain forge-gui/res/cardsfolder/d/diligent_farmhand.txt svneol=native#text/plain diff --git a/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java b/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java index c64ca3a682c..15a4fa6ef28 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java @@ -4,6 +4,7 @@ import com.google.common.base.Predicate; import forge.ai.ComputerUtil; import forge.ai.ComputerUtilCard; import forge.ai.SpellAbilityAi; +import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.card.CardPredicates.Presets; @@ -49,6 +50,11 @@ public class CopyPermanentAi extends SpellAbilityAi { final TargetRestrictions abTgt = sa.getTargetRestrictions(); if (abTgt != null) { + sa.resetTargets(); + if (sa.hasParam("TargetingPlayer")) { + Player targetingPlayer = AbilityUtils.getDefinedPlayers(source, sa.getParam("TargetingPlayer"), sa).get(0); + return targetingPlayer.getController().chooseTargetsFor(sa); + } List list = aiPlayer.getGame().getCardsIn(ZoneType.Battlefield); list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source); list = CardLists.getTargetableCards(list, sa); @@ -59,7 +65,6 @@ public class CopyPermanentAi extends SpellAbilityAi { return !vars.containsKey("RemAIDeck"); } }); - sa.resetTargets(); // target loop while (sa.getTargets().getNumTargeted() < abTgt.getMaxTargets(sa.getHostCard(), sa)) { if (list.isEmpty()) { diff --git a/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java b/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java index 066f99ce50e..2b4e0722261 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java @@ -7,6 +7,7 @@ import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCost; import forge.ai.SpellAbilityAi; import forge.game.ability.AbilityUtils; +import forge.game.ability.ApiType; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.card.CounterType; @@ -44,6 +45,7 @@ public class DestroyAi extends SpellAbilityAi { final TargetRestrictions abTgt = sa.getTargetRestrictions(); final Card source = sa.getHostCard(); final boolean noRegen = sa.hasParam("NoRegen"); + final String logic = sa.getParam("AILogic"); List list; if (abCost != null) { @@ -66,6 +68,10 @@ public class DestroyAi extends SpellAbilityAi { // Targeting if (abTgt != null) { sa.resetTargets(); + if (sa.hasParam("TargetingPlayer")) { + Player targetingPlayer = AbilityUtils.getDefinedPlayers(source, sa.getParam("TargetingPlayer"), sa).get(0); + return targetingPlayer.getController().chooseTargetsFor(sa); + } list = CardLists.getTargetableCards(ai.getOpponent().getCardsIn(ZoneType.Battlefield), sa); list = CardLists.getValidCards(list, abTgt.getValidTgts(), source.getController(), source); if (sa.hasParam("AITgts")) { @@ -129,6 +135,12 @@ public class DestroyAi extends SpellAbilityAi { // If the targets are only of one type, take the best if (CardLists.getNotType(list, "Creature").isEmpty()) { choice = ComputerUtilCard.getBestCreatureAI(list); + if ("OppDestroyYours".equals(logic)) { + Card aiBest = ComputerUtilCard.getBestCreatureAI(ai.getCreaturesInPlay()); + if (ComputerUtilCard.evaluateCreature(aiBest) > ComputerUtilCard.evaluateCreature(choice) - 40) { + return false; + } + } } else if (CardLists.getNotType(list, "Land").isEmpty()) { choice = ComputerUtilCard.getBestLandAI(list); } else { @@ -162,14 +174,12 @@ public class DestroyAi extends SpellAbilityAi { } else { if (sa.hasParam("Defined")) { list = new ArrayList(AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa)); - if (sa.hasParam("AILogic")) { - String logic = sa.getParam("AILogic"); - if ("WillSkipTurn".equals(logic) && !sa.getHostCard().getController().equals(ai) - && (ai.getCreaturesInPlay().size() >= ai.getOpponent().getCreaturesInPlay().size() || ai.getLife() > 5)) { - // Basic ai logic for Lethal Vapors - return true; - } + if ("WillSkipTurn".equals(logic) && !sa.getHostCard().getController().equals(ai) + && (ai.getCreaturesInPlay().size() >= ai.getOpponent().getCreaturesInPlay().size() || ai.getLife() > 5)) { + // Basic ai logic for Lethal Vapors + return true; } + if (list.isEmpty() || !CardLists.filterControlledBy(list, ai).isEmpty() || CardLists.getNotKeyword(list, "Indestructible").isEmpty()) { @@ -251,7 +261,13 @@ public class DestroyAi extends SpellAbilityAi { } else { Card c; if (CardLists.getNotType(list, "Creature").isEmpty()) { - c = ComputerUtilCard.getWorstCreatureAI(list); + if (!sa.getUniqueTargets().isEmpty() && sa.getParent().getApi() == ApiType.Destroy + && sa.getUniqueTargets().get(0) instanceof Card) { + // basic ai for Diaochan + c = (Card) sa.getUniqueTargets().get(0); + } else { + c = ComputerUtilCard.getWorstCreatureAI(list); + } } else { c = ComputerUtilCard.getCheapestPermanentAI(list, sa, false); } diff --git a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java index f1f916de93c..b56b82e1c43 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java @@ -258,6 +258,9 @@ public final class AbilityFactory { if (mapParams.containsKey("TargetsWithRelatedProperty")) { abTgt.setRelatedProperty(mapParams.get("TargetsWithRelatedProperty")); } + if (mapParams.containsKey("TargetingPlayer")) { + abTgt.setMandatory(true); + } return abTgt; } diff --git a/forge-gui/res/cardsfolder/d/diaochan_artful_beauty.txt b/forge-gui/res/cardsfolder/d/diaochan_artful_beauty.txt new file mode 100644 index 00000000000..551d17fdcdd --- /dev/null +++ b/forge-gui/res/cardsfolder/d/diaochan_artful_beauty.txt @@ -0,0 +1,8 @@ +Name:Diaochan, Artful Beauty +ManaCost:3 R +Types:Legendary Creature Human Advisor +PT:1/1 +A:AB$ Destroy | Cost$ T | ValidTgts$ Creature | SubAbility$ DBDestroy | AILogic$ OppDestroyYours | PlayerTurn$ True | ActivationPhases$ Upkeep->BeginCombat | SpellDescription$ Destroy target creature of your choice, then destroy target creature of an opponent's choice. Activate this ability only during your turn, before attackers are declared. +SVar:DBDestroy:DB$ Destroy | ValidTgts$ Creature | TargetingPlayer$ Opponent +SVar:Picture:http://www.wizards.com/global/images/magic/general/diaochan_artful_beauty.jpg +Oracle:{T}: Destroy target creature of your choice, then destroy target creature of an opponent's choice. Activate this ability only during your turn, before attackers are declared. diff --git a/forge-gui/src/main/java/forge/gui/input/InputSelectTargets.java b/forge-gui/src/main/java/forge/gui/input/InputSelectTargets.java index c67d96adfb9..dee82eba365 100644 --- a/forge-gui/src/main/java/forge/gui/input/InputSelectTargets.java +++ b/forge-gui/src/main/java/forge/gui/input/InputSelectTargets.java @@ -61,7 +61,10 @@ public final class InputSelectTargets extends InputSyncronizedBase { sb.append(" (").append(o.getValue()).append(" times)"); sb.append("\n"); } - + if (!sa.getUniqueTargets().isEmpty()) { + sb.append("Parent Targeted:"); + sb.append(sa.getUniqueTargets()).append("\n"); + } sb.append(sa.getHostCard() + " - " + tgt.getVTSelection()); int maxTargets = tgt.getMaxTargets(sa.getHostCard(), sa); diff --git a/forge-gui/src/main/java/forge/gui/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/gui/player/HumanPlaySpellAbility.java index fdbb6e8a6a5..c8d19f84d50 100644 --- a/forge-gui/src/main/java/forge/gui/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/gui/player/HumanPlaySpellAbility.java @@ -39,6 +39,7 @@ import forge.game.zone.Zone; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; +import java.util.List; import java.util.Map; /** @@ -139,9 +140,16 @@ public class HumanPlaySpellAbility { TargetRestrictions tgt = currentAbility.getTargetRestrictions(); if (tgt != null && tgt.doesTarget()) { clearTargets(currentAbility); - Player targetingPlayer = ability.hasParam("TargetingPlayer") ? - AbilityUtils.getDefinedPlayers(source, ability.getParam("TargetingPlayer"), currentAbility).get(0) : ability.getActivatingPlayer(); - + Player targetingPlayer; + if (currentAbility.hasParam("TargetingPlayer")) { + List candidates = AbilityUtils.getDefinedPlayers(source, currentAbility.getParam("TargetingPlayer"), currentAbility); + // activator chooses targeting player + targetingPlayer = ability.getActivatingPlayer().getController().chooseSingleEntityForEffect( + candidates, currentAbility, "Choose the targeting player"); + } else { + targetingPlayer = ability.getActivatingPlayer(); + } + if (!targetingPlayer.getController().chooseTargetsFor(currentAbility)) return false; }