diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index fa3744b2131..4305031989a 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -2077,18 +2077,23 @@ public class AiController { return result; } - public Collection complainCardsCantPlayWell(Deck myDeck) { - List result = Lists.newArrayList(); + public Map> complainCardsCantPlayWell(Deck myDeck) { + Map> complaints = new HashMap<>(); // When using simulation, AI should be able to figure out most cards. if (!useSimulation) { for (Entry ds : myDeck) { + List result = Lists.newArrayList(); for (Entry cp : ds.getValue()) { - if (cp.getKey().getRules().getAiHints().getRemAIDecks()) + if (cp.getKey().getRules().getAiHints().getRemAIDecks()) { result.add(cp.getKey()); + } + } + if (!result.isEmpty()) { + complaints.put(ds.getKey(), result); } } } - return result; + return complaints; } // this is where the computer cheats diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 31b4a46e648..7782b139151 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -28,6 +28,7 @@ import forge.card.MagicColor; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; import forge.deck.Deck; +import forge.deck.DeckSection; import forge.game.Game; import forge.game.GameEntity; import forge.game.GameObject; @@ -1125,7 +1126,12 @@ public class PlayerControllerAi extends PlayerController { } @Override - public Collection complainCardsCantPlayWell(Deck myDeck) { + public void revealAISkipCards(String message, Map>> deckCards) { + // Ai won't understand that anyway + } + + @Override + public Map> complainCardsCantPlayWell(Deck myDeck) { return brains.complainCardsCantPlayWell(myDeck); } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index d07fcafaf9c..327d6ac0c38 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -40,6 +40,7 @@ import com.google.common.collect.Sets; import forge.GameCommand; import forge.StaticData; import forge.card.CardStateName; +import forge.deck.DeckSection; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; @@ -1875,7 +1876,15 @@ public class GameAction { } } + public void revealUnplayableByAI(String title, Map>> unplayableCards) { + // Notify both players + for (Player p : game.getPlayers()) { + p.getController().revealAISkipCards(title, unplayableCards); + } + } + public void revealAnte(String title, Multimap removedAnteCards) { + // Notify both players for (Player p : game.getPlayers()) { p.getController().revealAnte(title, removedAnteCards); } diff --git a/forge-game/src/main/java/forge/game/Match.java b/forge-game/src/main/java/forge/game/Match.java index beac2accbb4..b7468054fd4 100644 --- a/forge-game/src/main/java/forge/game/Match.java +++ b/forge-game/src/main/java/forge/game/Match.java @@ -216,7 +216,7 @@ public class Match { game.getTriggerHandler().clearDelayedTrigger(); // friendliness - Multimap rAICards = HashMultimap.create(); + Map>> rAICards = new HashMap<>(); Multimap removedAnteCards = ArrayListMultimap.create(); final FCollectionView players = game.getPlayers(); @@ -315,9 +315,9 @@ public class Match { player.shuffle(null); if (isFirstGame) { - Collection cardsComplained = player.getController().complainCardsCantPlayWell(myDeck); - if (null != cardsComplained) { - rAICards.putAll(player, cardsComplained); + Map> cardsComplained = player.getController().complainCardsCantPlayWell(myDeck); + if (cardsComplained != null && !cardsComplained.isEmpty()) { + rAICards.put(player, cardsComplained); } } else { //reset cards to fix weird issues on netplay nextgame client @@ -334,7 +334,7 @@ public class Match { final Localizer localizer = Localizer.getInstance(); if (!rAICards.isEmpty() && !rules.getGameType().isCardPoolLimited()) { - game.getAction().revealAnte(localizer.getMessage("lblAICantPlayCards"), rAICards); + game.getAction().revealUnplayableByAI(localizer.getMessage("lblAICantPlayCards"), rAICards); } if (!removedAnteCards.isEmpty()) { diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index 54bf45aaa06..b596ce17c6c 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -17,6 +17,7 @@ import forge.card.ICardFace; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; import forge.deck.Deck; +import forge.deck.DeckSection; import forge.game.Game; import forge.game.GameEntity; import forge.game.GameObject; @@ -238,10 +239,11 @@ public abstract class PlayerController { public abstract boolean chooseCardsPile(SpellAbility sa, CardCollectionView pile1, CardCollectionView pile2, String faceUp); public abstract void revealAnte(String message, Multimap removedAnteCards); + public abstract void revealAISkipCards(String message, Map>> deckCards); // These 2 are for AI public CardCollectionView cheatShuffle(CardCollectionView list) { return list; } - public Collection complainCardsCantPlayWell(Deck myDeck) { return null; } + public Map> complainCardsCantPlayWell(Deck myDeck) { return null; } public abstract void resetAtEndOfTurn(); // currently used by the AI to perform card memory cleanup diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index b3ad4fb7957..9fef4977be7 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -19,6 +19,7 @@ import forge.card.MagicColor; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; import forge.deck.Deck; +import forge.deck.DeckSection; import forge.game.Game; import forge.game.GameEntity; import forge.game.GameObject; @@ -594,6 +595,11 @@ public class PlayerControllerForTests extends PlayerController { // test this! } + @Override + public void revealAISkipCards(final String message, final Map> removedUnplayableCards) { + // TODO test this! + } + @Override public List chooseCardsYouWonToAddToDeck(List losses) { // TODO Auto-generated method stub diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index ca52b367b4d..bbf470b09e9 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -17,6 +17,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.stream.Collectors; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Range; @@ -2088,6 +2089,34 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } } + @Override + public void revealAISkipCards(final String message, final Map>> unplayable) { + for (Player p : unplayable.keySet()) { + final Map> removedUnplayableCards = unplayable.get(p); + final List labels = new ArrayList<>(); + for (final DeckSection s: removedUnplayableCards.keySet()) { + labels.add("=== " + getLocalizedDeckSection(s) + " ==="); + labels.addAll(removedUnplayableCards.get(s).stream().map(a -> a.toString()).collect(Collectors.toList())); + } + getGui().reveal(localizer.getMessage("lblActionFromPlayerDeck", message, Lang.getInstance().getPossessedObject(MessageUtil.mayBeYou(player, p), "")), + ImmutableList.copyOf(labels)); + } + } + + private String getLocalizedDeckSection(DeckSection d) { + switch (d) { + case Avatar: return localizer.getMessage("lblAvatar"); + case Commander: return localizer.getMessage("lblCommanderDeck"); + case Main: return localizer.getMessage("lblMainDeck"); + case Sideboard: return localizer.getMessage("lblSideboard"); + case Planes: return localizer.getMessage("lblPlanarDeck"); + case Schemes: return localizer.getMessage("lblSchemeDeck"); + case Conspiracy: return /* TODO localise */ "Conspiracy"; + case Dungeon: return /* TODO localise */ "Dungeon"; + default: return /* TODO better handling */ "UNKNOWN"; + } + } + @Override public List chooseCardsYouWonToAddToDeck(final List losses) { return getGui().many(localizer.getMessage("lblSelectCardstoAddtoYourDeck"), localizer.getMessage("lblAddTheseToMyDeck"), 0, losses.size(), losses, null);