Add a feature to be able to Sideboard for the AI

This commit is contained in:
Chris H
2019-05-30 22:55:31 -04:00
parent f384e1dd99
commit cd20443255
17 changed files with 67 additions and 23 deletions

View File

@@ -91,7 +91,7 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public List<PaperCard> sideboard(Deck deck, GameType gameType) {
public List<PaperCard> sideboard(Deck deck, GameType gameType, String message) {
// AI does not know how to sideboard
return null;
}

View File

@@ -11,6 +11,7 @@ public class GameRules {
private int gamesToWinMatch = 2;
private boolean playForAnte = false;
private boolean matchAnteRarity = false;
private boolean sideboardForAI = false;
private final Set<GameType> appliedVariants = EnumSet.noneOf(GameType.class);
// it's a preference, not rule... but I could hardly find a better place for it
@@ -65,6 +66,14 @@ public class GameRules {
matchAnteRarity = matchRarity;
}
public boolean getSideboardForAI() {
return sideboardForAI;
}
public void setSideboardForAI(final boolean sideboard) {
sideboardForAI = sideboard;
}
public int getGamesToWinMatch() {
return gamesToWinMatch;
}

View File

@@ -4,12 +4,14 @@ import com.google.common.collect.*;
import forge.LobbyPlayer;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckFormat;
import forge.deck.DeckSection;
import forge.game.card.Card;
import forge.game.card.CardCollectionView;
import forge.game.event.GameEventAnteCardsSelected;
import forge.game.event.GameEventGameFinished;
import forge.game.player.Player;
import forge.game.player.PlayerController;
import forge.game.player.RegisteredPlayer;
import forge.game.trigger.Trigger;
import forge.game.zone.PlayerZone;
@@ -208,18 +210,39 @@ public class Match {
Multimap<Player, PaperCard> rAICards = HashMultimap.create();
Multimap<Player, PaperCard> removedAnteCards = ArrayListMultimap.create();
boolean isFirstGame = game.getMatch().getPlayedGames().isEmpty();
boolean canSideBoard = !isFirstGame && rules.getGameType().isSideboardingAllowed();
final FCollectionView<Player> players = game.getPlayers();
final List<RegisteredPlayer> playersConditions = game.getMatch().getPlayers();
boolean isFirstGame = game.getMatch().getPlayedGames().isEmpty();
boolean canSideBoard = !isFirstGame && rules.getGameType().isSideboardingAllowed();
// Only allow this if feature flag is on AND for certain match types
boolean sideboardForAIs = rules.getSideboardForAI() &&
rules.getGameType().getDeckFormat().equals(DeckFormat.Constructed);
PlayerController sideboardProxy = null;
if (canSideBoard && sideboardForAIs) {
for (int i = 0; i < playersConditions.size(); i++) {
final Player player = players.get(i);
final RegisteredPlayer psc = playersConditions.get(i);
if (!player.getController().isAI()) {
sideboardProxy = player.getController();
break;
}
}
}
for (int i = 0; i < playersConditions.size(); i++) {
final Player player = players.get(i);
final RegisteredPlayer psc = playersConditions.get(i);
if (canSideBoard) {
PlayerController person = player.getController();
if (sideboardProxy != null && person.isAI()) {
person = sideboardProxy;
}
String forPlayer = " for " + player.getName();
Deck toChange = psc.getDeck();
List<PaperCard> newMain = player.getController().sideboard(toChange, rules.getGameType());
List<PaperCard> newMain = person.sideboard(toChange, rules.getGameType(), forPlayer);
if (null != newMain) {
CardPool allCards = new CardPool();
allCards.addAll(toChange.get(DeckSection.Main));

View File

@@ -91,7 +91,7 @@ public abstract class PlayerController {
public abstract void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets);
public abstract void playSpellAbilityNoStack(SpellAbility effectSA, boolean mayChoseNewTargets);
public abstract List<PaperCard> sideboard(final Deck deck, GameType gameType);
public abstract List<PaperCard> sideboard(final Deck deck, GameType gameType, String message);
public abstract List<PaperCard> chooseCardsYouWonToAddToDeck(List<PaperCard> losses);
public abstract Map<Card, Integer> assignCombatDamage(Card attacker, CardCollectionView blockers, int damageDealt, GameEntity defender, boolean overrideOrder);

View File

@@ -234,10 +234,10 @@ public class GuiChoose {
return null;
}
public static <T extends Comparable<? super T>> List<T> sideboard(final CMatchUI matchUI, final List<T> sideboard, final List<T> deck) {
public static <T extends Comparable<? super T>> List<T> sideboard(final CMatchUI matchUI, final List<T> sideboard, final List<T> deck, final String message) {
Collections.sort(deck);
Collections.sort(sideboard);
return order("Sideboard", "Main Deck", -1, -1, sideboard, deck, null, true, matchUI);
return order("Sideboard" + message, "Main Deck", -1, -1, sideboard, deck, null, true, matchUI);
}
public static <T> List<T> order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax,

View File

@@ -107,6 +107,7 @@ public enum CSubmenuPreferences implements ICDoc {
lstControls.add(Pair.of(view.getCbRandomArtInPools(), FPref.UI_RANDOM_ART_IN_POOLS));
lstControls.add(Pair.of(view.getCbEnforceDeckLegality(), FPref.ENFORCE_DECK_LEGALITY));
lstControls.add(Pair.of(view.getCbPerformanceMode(), FPref.PERFORMANCE_MODE));
lstControls.add(Pair.of(view.getCbSideboardForAI(), FPref.MATCH_SIDEBOARD_FOR_AI));
lstControls.add(Pair.of(view.getCbFilteredHands(), FPref.FILTERED_HANDS));
lstControls.add(Pair.of(view.getCbCloneImgSource(), FPref.UI_CLONE_MODE_SOURCE));
lstControls.add(Pair.of(view.getCbRemoveSmall(), FPref.DECKGEN_NOSMALL));

View File

@@ -75,6 +75,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
private final JCheckBox cbLoadHistoricFormats = new OptionsCheckBox(localizer.getMessage("cbLoadHistoricFormats"));
private final JCheckBox cbWorkshopSyntax = new OptionsCheckBox(localizer.getMessage("cbWorkshopSyntax"));
private final JCheckBox cbEnforceDeckLegality = new OptionsCheckBox(localizer.getMessage("cbEnforceDeckLegality"));
private final JCheckBox cbSideboardForAI = new OptionsCheckBox(localizer.getMessage("cbSideboardForAI"));
private final JCheckBox cbPerformanceMode = new OptionsCheckBox(localizer.getMessage("cbPerformanceMode"));
private final JCheckBox cbFilteredHands = new OptionsCheckBox(localizer.getMessage("cbFilteredHands"));
private final JCheckBox cbImageFetcher = new OptionsCheckBox(localizer.getMessage("cbImageFetcher"));
@@ -195,6 +196,9 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
pnlPrefs.add(cbPerformanceMode, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlPerformanceMode")), descriptionConstraints);
pnlPrefs.add(cbSideboardForAI, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlSideboardForAI")), descriptionConstraints);
pnlPrefs.add(cbFilteredHands, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlFilteredHands")), descriptionConstraints);
@@ -680,6 +684,10 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
return cbPerformanceMode;
}
public JCheckBox getCbSideboardForAI() {
return cbSideboardForAI;
}
/** @return {@link javax.swing.JCheckBox} */
public JCheckBox getCbFilteredHands() {
return cbFilteredHands;

View File

@@ -1022,8 +1022,8 @@ public final class CMatchUI
}
@Override
public List<PaperCard> sideboard(final CardPool sideboard, final CardPool main) {
return GuiChoose.sideboard(this, sideboard.toFlatList(), main.toFlatList());
public List<PaperCard> sideboard(final CardPool sideboard, final CardPool main, final String message) {
return GuiChoose.sideboard(this, sideboard.toFlatList(), main.toFlatList(), message);
}
@Override

View File

@@ -112,7 +112,7 @@ public class PlayerControllerForTests extends PlayerController {
}
@Override
public List<PaperCard> sideboard(Deck deck, GameType gameType) {
public List<PaperCard> sideboard(Deck deck, GameType gameType, String message) {
return null; // refused to side
}

View File

@@ -23,8 +23,8 @@ public class FSideboardDialog extends FDialog {
private final SideboardTabs tabs;
private final Callback<List<PaperCard>> callback;
public FSideboardDialog(CardPool sideboard, CardPool main, final Callback<List<PaperCard>> callback0) {
super("Update main deck from sideboard", 1);
public FSideboardDialog(CardPool sideboard, CardPool main, final Callback<List<PaperCard>> callback0, String message) {
super("Update main deck from sideboard" + message, 1);
callback = callback0;
tabs = add(new SideboardTabs(sideboard, main));

View File

@@ -481,11 +481,11 @@ public class MatchController extends AbstractGuiGame {
}
@Override
public List<PaperCard> sideboard(final CardPool sideboard, final CardPool main) {
public List<PaperCard> sideboard(final CardPool sideboard, final CardPool main, final String message) {
return new WaitCallback<List<PaperCard>>() {
@Override
public void run() {
final FSideboardDialog sideboardDialog = new FSideboardDialog(sideboard, main, this);
final FSideboardDialog sideboardDialog = new FSideboardDialog(sideboard, main, this, message);
sideboardDialog.show();
}
}.invokeAndWait();

View File

@@ -33,6 +33,7 @@ cbLoadCardsLazily = Load Card Scripts Lazily
cbLoadHistoricFormats = Load Historic Formats
cbWorkshopSyntax = Workshop Syntax Checker
cbEnforceDeckLegality = Deck Conformance
cbSideboardForAI = Human Sideboard for AI
cbPerformanceMode = Performance Mode
cbFilteredHands = Filtered Hands
cbImageFetcher = Automatically Download Missing Card Art
@@ -86,6 +87,7 @@ nlEnableAICheats = Allow the AI to cheat to gain advantage (for personalities th
nlManaBurn = Play with mana burn (from pre-Magic 2010 rules).
nlManaLostPrompt = When enabled, you get a warning if passing priority would cause you to lose mana in your mana pool.
nlEnforceDeckLegality = Enforces deck legality relevant to each environment (minimum deck sizes, max card count etc).
nlSideboardForAI = Allows users to sideboard with the AIs deck and sideboard in constructed game formats.
nlPerformanceMode = Disables additional static abilities checks to speed up the game engine. (Warning: breaks some 'as if had flash' scenarios when casting cards owned by opponents).
nlFilteredHands = Generates two starting hands and keeps the one with the closest to average land count for the deck. (Requires restart)
nlCloneImgSource = When enabled clones will use their original art instead of the cloned card's art.

View File

@@ -144,7 +144,7 @@ public interface IGuiGame {
*/
<T> List<T> insertInList(String title, T newItem, List<T> oldItems);
List<PaperCard> sideboard(CardPool sideboard, CardPool main);
List<PaperCard> sideboard(CardPool sideboard, CardPool main, String message);
GameEntityView chooseSingleEntityForEffect(String title, List<? extends GameEntityView> optionList, DelayedReveal delayedReveal, boolean isOptional);
List<GameEntityView> chooseEntitiesForEffect(String title, List<? extends GameEntityView> optionList, int min, int max, DelayedReveal delayedReveal);

View File

@@ -79,6 +79,7 @@ public class HostedMatch {
gameRules.setPlayForAnte(FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE));
gameRules.setMatchAnteRarity(FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE_MATCH_RARITY));
gameRules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
gameRules.setSideboardForAI(FModel.getPreferences().getPrefBoolean(FPref.MATCH_SIDEBOARD_FOR_AI));
gameRules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE));
return gameRules;
}

View File

@@ -234,8 +234,8 @@ public class NetGuiGame extends AbstractGuiGame {
}
@Override
public List<PaperCard> sideboard(final CardPool sideboard, final CardPool main) {
return sendAndWait(ProtocolMethod.sideboard, sideboard, main);
public List<PaperCard> sideboard(final CardPool sideboard, final CardPool main, final String message) {
return sendAndWait(ProtocolMethod.sideboard, sideboard, main, message);
}
@Override

View File

@@ -209,11 +209,10 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
}
@Override
public List<PaperCard> sideboard(final Deck deck, final GameType gameType) {
public List<PaperCard> sideboard(final Deck deck, final GameType gameType, String message) {
CardPool sideboard = deck.get(DeckSection.Sideboard);
if (sideboard == null) {
// Use an empty cardpool instead of null for 75/0 sideboarding
// scenario.
// Use an empty cardpool instead of null for 75/0 sideboarding scenario.
sideboard = new CardPool();
}
@@ -253,7 +252,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
// Sideboard rules have changed for M14, just need to consider min
// maindeck and max sideboard sizes
// No longer need 1:1 sideboarding in non-limited formats
Object resp = getGui().sideboard(sideboard, main);
Object resp = getGui().sideboard(sideboard, main, message);
if (resp instanceof List<?> &&
!((List) resp).isEmpty() &&
((List) resp).get(0) instanceof PaperCard) {

View File

@@ -156,6 +156,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
SUBMENU_SETTINGS ("false"),
SUBMENU_UTILITIES ("false"),
MATCH_SIDEBOARD_FOR_AI("true"), // TODO What do when AI knows how to SIdeboard?
ENFORCE_DECK_LEGALITY ("true"),
PERFORMANCE_MODE ("false"),
FILTERED_HANDS ("false"),