mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 03:08:02 +00:00
Add a feature to be able to Sideboard for the AI
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
@@ -147,7 +148,7 @@ public enum CSubmenuPreferences implements ICDoc {
|
||||
|
||||
|
||||
for(final Pair<JCheckBox, FPref> kv : lstControls) {
|
||||
kv.getKey().addItemListener(new ItemListener() {
|
||||
kv.getKey().addItemListener(new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(final ItemEvent arg0) {
|
||||
if (updating) { return; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"),
|
||||
|
||||
Reference in New Issue
Block a user