Huge update to the GUI codebase.

- Make match screen in gui-desktop non-singleton, allowing multiple games to be played simultaneously.
- Separate global gui commands (IGuiBase) from game-specific gui commands (IGuiGame), allowing games to send their commands to the correct gui.
This commit is contained in:
elcnesh
2015-02-17 08:47:55 +00:00
parent 4ac5d67a02
commit 4a38b0d817
244 changed files with 5532 additions and 3554 deletions

View File

@@ -18,7 +18,6 @@ import forge.game.Game;
import forge.game.GameType;
import forge.game.player.Player;
import forge.interfaces.IComboBox;
import forge.match.MatchUtil;
import forge.model.FModel;
import forge.player.PlayerControllerHuman;
import forge.properties.ForgeConstants;
@@ -35,9 +34,9 @@ public abstract class AchievementCollection implements Iterable<Achievement> {
FileUtil.ensureDirectoryExists(ForgeConstants.ACHIEVEMENTS_DIR);
}
public static void updateAll(PlayerControllerHuman controller) {
//don't update achievements if player cheated during game or if it's not just a single human player
if (controller.hasCheated() || MatchUtil.getHumanCount() != 1) {
public static void updateAll(final PlayerControllerHuman controller) {
//don't update achievements if player cheated during game
if (controller.hasCheated()) {
return;
}

View File

@@ -5,11 +5,11 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Sets;
import forge.game.GameView;
import forge.game.card.CardUtil;
import forge.game.card.CardView;
import forge.game.card.CardView.CardStateView;
@@ -17,7 +17,6 @@ import forge.game.card.CounterType;
import forge.item.InventoryItemFromSet;
import forge.item.PreconDeck;
import forge.item.SealedProduct;
import forge.match.MatchUtil;
import forge.util.Lang;
public class CardDetailUtil {
@@ -477,8 +476,10 @@ public class CardDetailUtil {
}
//show current storm count for storm cards
// TODO add Storm
/*
if (state.hasStorm()) {
GameView gameView = MatchUtil.getGameView();
GameView gameView = HostedMatch.getGameView();
if (gameView != null) {
if (area.length() != 0) {
area.append("\n\n");
@@ -486,6 +487,7 @@ public class CardDetailUtil {
area.append("Current Storm Count: " + gameView.getStormCount());
}
}
*/
return area.toString();
}
}

View File

@@ -0,0 +1,522 @@
package forge.card;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import forge.game.ability.ApiType;
import forge.game.trigger.TriggerType;
public final class CardScriptParser {
private final String script;
private final Set<String> sVars = Sets.newTreeSet(), sVarAbilities = Sets.newTreeSet();
public CardScriptParser(final String script) {
this.script = script;
final String[] lines = StringUtils.split(script, "\r\n");
for (final String line : lines) {
if (StringUtils.isEmpty(line)) {
continue;
}
if (line.startsWith("SVar:")) {
final String[] sVarParts = StringUtils.split(line, ':');
if (sVarParts.length != 3) {
continue;
}
sVars.add(sVarParts[1]);
}
}
}
public boolean hasErrors() {
return !getErrorRegions(true).isEmpty();
}
public Map<Integer, Integer> getErrorRegions() {
return getErrorRegions(false);
}
/**
* Find all erroneous regions of this script.
*
* @param quick
* if {@code true}, stop when the first region is found.
* @return a {@link Map} mapping the starting index of each error region to
* the length of that region.
*/
private Map<Integer, Integer> getErrorRegions(final boolean quick) {
final Map<Integer, Integer> result = Maps.newTreeMap();
final String[] lines = StringUtils.split(script, '\n');
int index = 0;
for (final String line : lines) {
final String trimLine = line.trim();
if (StringUtils.isEmpty(line)) {
continue;
}
boolean bad = false;
if (trimLine.startsWith("Name:") && trimLine.length() > "Name:".length()) {
// whatever, nonempty name is always ok!
} else if (trimLine.startsWith("ManaCost:")) {
if (!isManaCostLegal(trimLine.substring("ManaCost:".length()))) {
bad = true;
}
} else if (trimLine.startsWith("Types:")) {
if (!isTypeLegal(trimLine.substring("Types:".length()))) {
bad = true;
}
} else if (trimLine.startsWith("A:")) {
result.putAll(getActivatedAbilityErrors(trimLine.substring("A:".length()), index + "A:".length()));
} else if (trimLine.startsWith("T:")) {
result.putAll(getTriggerErrors(trimLine.substring("T:".length()), index + "T:".length()));
} else if (trimLine.startsWith("SVar:")) {
final String[] sVarParts = trimLine.split(":", 3);
if (sVarParts.length != 3) {
bad = true;
}
if (sVarAbilities.contains(sVarParts[1])) {
result.putAll(getSubAbilityErrors(sVarParts[2], index + "SVar:".length() + 1 + sVarParts[1].length() + 1));
}
}
if (bad) {
result.put(index, trimLine.length());
}
index += line.length() + 1;
if (quick && !result.isEmpty()) {
break;
}
}
return result;
}
private static boolean isManaCostLegal(final String manaCost) {
if (manaCost.equals("no cost")) {
return true;
}
if (StringUtils.isEmpty(manaCost) || StringUtils.isWhitespace(manaCost)) {
return false;
}
for (final String part : StringUtils.split(manaCost, ' ')) {
if (StringUtils.isNumeric(part) || part.equals("X")) {
continue;
}
return isManaCostPart(part);
}
return true;
}
private static boolean isManaCostPart(final String part) {
if (part.length() == 1) {
return isManaSymbol(part.charAt(0));
} else if (part.length() == 2) {
if (!(part.startsWith("P") || part.startsWith("2") || isManaSymbol(part.charAt(0)))) {
return false;
}
if ((!isManaSymbol(part.charAt(1))) || part.charAt(0) == part.charAt(1)) {
return false;
}
return true;
}
return false;
}
private static boolean isManaSymbol(final char c) {
return c == 'W' || c == 'U' || c == 'B' || c == 'R' || c == 'G';
}
private static boolean isTypeLegal(final String type) {
for (final String t : StringUtils.split(type, ' ')) {
if (!isSingleTypeLegal(t)) {
return false;
}
}
return true;
}
private static boolean isSingleTypeLegal(final String type) {
return CardType.isACardType(type) || CardType.isASupertype(type) || CardType.isASubType(type);
}
private static List<KeyValuePair> getParams(final String ability, final int offset, final Map<Integer, Integer> errorRegions) {
final String[] parts = StringUtils.split(ability, '|');
final List<KeyValuePair> params = Lists.newArrayList();
int currentIndex = offset;
for (final String part : parts) {
final String[] subParts = StringUtils.split(part, '$');
if (subParts.length > 0) {
params.add(new KeyValuePair(subParts[0], subParts.length > 1 ? subParts[1] : "", currentIndex));
} else {
errorRegions.put(currentIndex, part.length());
}
currentIndex += part.length() + 1;
}
// Check spacing
for (final KeyValuePair param : params) {
if (!param.getKey().startsWith(" ") && param.startIndex() != offset) {
errorRegions.put(param.startIndex() - 1, 2);
}
if (!param.getValue().startsWith(" ")) {
errorRegions.put(param.startIndexValue() - 1, 2);
}
if (!param.getValue().endsWith(" ") && param.endIndex() != offset + ability.length()) {
errorRegions.put(param.endIndex() - 1, 2);
}
}
return params;
}
private Map<Integer, Integer> getActivatedAbilityErrors(final String ability, final int offset) {
return getAbilityErrors(ability, offset, true);
}
private Map<Integer, Integer> getSubAbilityErrors(final String ability, final int offset) {
return getAbilityErrors(ability, offset, false);
}
private Map<Integer, Integer> getAbilityErrors(final String ability, final int offset, final boolean requireCost) {
final Map<Integer, Integer> result = Maps.newTreeMap();
final List<KeyValuePair> params = getParams(ability, offset, result);
// First parameter should be Api declaration
if (!isAbilityApiDeclarerLegal(params.get(0).getKey())) {
result.put(params.get(0).startIndex(), params.get(0).length());
}
// If present, second parameter should be cost
if (requireCost && !params.get(1).getKey().trim().equals("Cost")) {
result.put(params.get(1).startIndex(), params.get(1).length());
}
// Now, check all parameters
for (final KeyValuePair param : params) {
boolean isBadValue = false;
final String trimKey = param.getKey().trim(), trimValue = param.getValue().trim();
if (isAbilityApiDeclarerLegal(trimKey)) {
if (!isAbilityApiLegal(trimValue)) {
isBadValue = true;
}
} else if (trimKey.equals("Cost")) {
if (!isCostLegal(trimValue)) {
isBadValue = true;
}
} else if (trimKey.equals("ValidTgts") || trimKey.equals("ValidCards")) {
if (!isValidLegal(trimValue)) {
isBadValue = true;
}
} else if (trimKey.equals("Defined")) {
if (!isDefinedLegal(trimValue)) {
isBadValue = true;
}
} else if (trimKey.equals("TgtPrompt") || trimKey.equals("StackDescription") || trimKey.equals("SpellDescription")) {
if (trimValue.isEmpty()) {
isBadValue = true;
}
} else if (trimKey.equals("SubAbility")) {
if (sVars.contains(trimValue)) {
sVarAbilities.add(trimValue);
} else {
isBadValue = true;
}
} else {
result.put(param.startIndex(), param.keyLength());
}
if (isBadValue) {
result.put(param.startIndexValue(), param.valueLength());
}
}
return result;
}
private Map<Integer, Integer> getTriggerErrors(final String trigger, final int offset) {
final Map<Integer, Integer> result = Maps.newTreeMap();
final List<KeyValuePair> params = getParams(trigger, offset, result);
// Check all parameters
for (final KeyValuePair param : params) {
boolean isBadValue = false;
final String trimKey = param.getKey().trim(), trimValue = param.getValue().trim();
if (trimKey.equals("Mode")) {
if (!isTriggerApiLegal(trimValue)) {
isBadValue = true;
}
} else if (trimKey.equals("Cost")) {
if (!isCostLegal(trimValue)) {
isBadValue = true;
}
} else if (trimKey.equals("Execute")) {
if (sVars.contains(trimValue)) {
sVarAbilities.add(trimValue);
} else {
isBadValue = true;
}
} else if (trimKey.equals("TriggerDescription")) {
if (trimValue.isEmpty()) {
//isBadValue = true;
}
} else if (trimKey.equals("ValidCard")) {
if (!isValidLegal(trimValue)) {
isBadValue = true;
}
} else {
result.put(param.startIndex(), param.keyLength());
}
if (isBadValue) {
result.put(param.startIndexValue(), param.valueLength());
}
}
return result;
}
private static boolean isCostLegal(final String cost) {
return isManaCostLegal(cost.trim()); // TODO include other costs (tap, sacrifice, etc.)
}
private static boolean isAbilityApiDeclarerLegal(final String declarer) {
final String tDeclarer = declarer.trim();
return tDeclarer.equals("AB") || tDeclarer.equals("DB") || tDeclarer.equals("SP");
}
private static boolean isAbilityApiLegal(final String api) {
try {
return ApiType.smartValueOf(api.trim()) != null;
} catch (final RuntimeException e) {
return false;
}
}
private static boolean isTriggerApiLegal(final String api) {
try {
return TriggerType.smartValueOf(api.trim()) != null;
} catch (final RuntimeException e) {
return false;
}
}
private static final Predicate<String> startsWith(final String s) {
return new Predicate<String>() {
public boolean apply(final String input) {
return s.startsWith(input);
}};
}
/**
* Literal defined strings for cards and spellabilities.
*/
private static final Set<String> DEFINED_CARDS = ImmutableSortedSet.of(
"Self", "OriginalHost", "EffectSource", "Equipped", "Enchanted",
"TopOfLibrary", "BottomOfLibrary", "Targeted", "ThisTargetedCard",
"ParentTarget", "Remembered", "DirectRemembered",
"DelayTriggerRemembered", "FirstRemembered", "Clones", "Imprinted",
"ChosenCard", "SacrificedCards", "Sacrificed", "DiscardedCards",
"Discarded", "ExiledCards", "Exiled", "TappedCards", "Tapped",
"UntappedCards", "Untapped", "Parent", "SourceFirstSpell");
/**
* Defined starting strings for cards and spellabilities.
*/
private static final Set<String> DEFINED_CARDS_STARTSWITH = ImmutableSortedSet
.of("Triggered", "Replaced", "ThisTurnEntered");
/**
* Literal defined strings for players.
*/
private static final Set<String> DEFINED_PLAYERS = ImmutableSortedSet.of(
"Targeted", "TargetedPlayer", "ParentTarget", "TargetedController",
"TargetedOwner", "TargetedAndYou", "ParentTargetedController",
"Remembered", "DelayTriggerRemembered", "RememberedOpponents",
"RememberedController", "RememberedOwner", "ImprintedController",
"ImprintedOwner", "EnchantedController", "EnchantedOwner",
"EnchantedPlayer", "AttackingPlayer", "DefendingPlayer",
"ChosenPlayer", "ChosenAndYou", "SourceController", "CardOwner",
"ActivePlayer", "You", "Each", "Opponent");
/**
* Defined starting strings for players.
*/
private static final Set<String> DEFINED_PLAYERS_STARTSWITH = ImmutableSortedSet
.of("Triggered", "OppNonTriggered", "Replaced");
private static boolean isDefinedLegal(final String defined) {
return isDefinedCardOrSaLegal(defined) || isDefinedPlayerLegal(defined);
}
private static boolean isDefinedCardOrSaLegal(final String defined) {
if (defined.startsWith("Valid")) {
return isValidLegal(defined.substring("Valid".length()));
}
if (DEFINED_CARDS.contains(defined)) {
return true;
}
if (Iterables.any(DEFINED_CARDS_STARTSWITH, startsWith(defined))) {
return true;
}
return false;
}
private static boolean isDefinedPlayerLegal(final String defined) {
final boolean non = defined.startsWith("Non"), flipped = defined.startsWith("Flipped");
if (non || flipped) {
String newDefined = null;
if (non) {
newDefined = defined.substring("Non".length());
} else if (flipped) {
newDefined = defined.substring("Flipped".length());
}
return isDefinedPlayerLegal(newDefined);
}
if (DEFINED_PLAYERS.contains(defined)) {
return true;
}
if (Iterables.any(DEFINED_PLAYERS_STARTSWITH, startsWith(defined))) {
return true;
}
return false;
}
private static final Set<String> VALID_INCLUSIVE = ImmutableSortedSet.of(
"Spell", "Permanent", "Card");
private static boolean isValidLegal(final String valid) {
String remaining = valid;
if (remaining.charAt(0) == '!') {
remaining = valid.substring(1);
}
final String[] splitDot = remaining.split("\\.");
if (!(VALID_INCLUSIVE.contains(splitDot[0]) || isSingleTypeLegal(splitDot[0]))) {
return false;
}
if (splitDot.length < 2) {
return true;
}
final String[] splitPlus = StringUtils.split(splitDot[1], '+');
for (final String excl : splitPlus) {
if (!isValidExclusive(excl)) {
return false;
}
}
return true;
}
private static final Set<String> VALID_EXCLUSIVE = ImmutableSortedSet.of(
"sameName", "namedCard", "NamedByRememberedPlayer", "Permanent",
"ChosenCard", "nonChosenCard", "White", "Blue", "Black", "Red",
"Green", "nonWhite", "nonBlue", "nonBlack", "nonRed", "nonGreen",
"Colorless", "nonColorless", "Multicolor", "nonMulticolor",
"Monocolor", "nonMonocolor", "ChosenColor", "AllChosenColors",
"AnyChosenColor", "DoubleFaced", "Flip", "YouCtrl", "YouDontCtrl",
"OppCtrl", "ChosenCtrl", "DefenderCtrl",
"DefenderCtrlForRemembered", "DefendingPlayerCtrl",
"EnchantedPlayerCtrl", "EnchantedControllerCtrl",
"RememberedPlayer", "RememberedPlayerCtrl",
"nonRememberedPlayerCtrl", "TargetedPlayerCtrl",
"TargetedControllerCtrl", "ActivePlayerCtrl",
"NonActivePlayerCtrl", "YouOwn", "YouDontOwn", "OppOwn",
"TargetedPlayerOwn", "OwnerDoesntControl", "Other", "Self",
"AttachedBy", "Attached", "NameNotEnchantingEnchantedPlayer",
"NotAttachedTo", "Enchanted", "CanEnchantRemembered",
"CanEnchantSource", "CanBeEnchantedBy", "CanBeEnchantedByTargeted",
"CanBeEnchantedByAllRemembered", "EquippedBy",
"EquippedByTargeted", "EquippedByEnchanted", "FortifiedBy",
"CanBeEquippedBy", "Equipped", "Fortified", "HauntedBy",
"notTributed", "madness", "Paired", "NotPaired", "PairedWith",
"Above", "DirectlyAbove", "TopGraveyardCreature",
"BottomGraveyard", "TopLibrary", "Cloned", "DamagedBy", "Damaged",
"IsTargetingSource", "sharesPermanentTypeWith",
"canProduceSameManaTypeWith", "SecondSpellCastThisTurn",
"ThisTurnCast", "withFlashback", "tapped", "untapped", "faceDown",
"faceUp", "hasLevelUp", "DrawnThisTurn",
"enteredBattlefieldThisTurn", "notEnteredBattlefieldThisTurn",
"firstTurnControlled", "notFirstTurnControlled",
"startedTheTurnUntapped", "attackedOrBlockedSinceYourLastUpkeep",
"blockedOrBeenBlockedSinceYourLastUpkeep",
"dealtDamageToYouThisTurn", "dealtDamageToOppThisTurn",
"controllerWasDealtCombatDamageByThisTurn",
"controllerWasDealtDamageByThisTurn", "wasDealtDamageThisTurn",
"wasDealtDamageByHostThisTurn", "wasDealtDamageByEquipeeThisTurn",
"wasDealtDamageByEnchantedThisTurn", "dealtDamageThisTurn",
"attackedThisTurn", "attackedLastTurn", "blockedThisTurn",
"gotBlockedThisTurn", "notAttackedThisTurn", "notAttackedLastTurn",
"notBlockedThisTurn", "greatestPower", "yardGreatestPower",
"leastPower", "leastToughness", "greatestCMC",
"greatestRememberedCMC", "lowestRememberedCMC", "lowestCMC",
"enchanted", "unenchanted", "enchanting", "equipped", "unequipped",
"equipping", "token", "nonToken", "hasXCost", "suspended",
"delved", "attacking", "attackingYou", "notattacking",
"attackedBySourceThisCombat", "blocking", "blockingSource",
"blockingCreatureYouCtrl", "blockingRemembered",
"sharesBlockingAssignmentWith", "notblocking", "blocked",
"blockedBySource", "blockedThisTurn", "blockedByThisTurn",
"blockedBySourceThisTurn", "blockedSource",
"isBlockedByRemembered", "blockedRemembered",
"blockedByRemembered", "unblocked", "attackersBandedWith",
"kicked", "kicked1", "kicked2", "notkicked", "evoked",
"HasDevoured", "HasNotDevoured", "IsMonstrous", "IsNotMonstrous",
"CostsPhyrexianMana", "IsRemembered", "IsNotRemembered",
"IsImprinted", "IsNotImprinted", "hasActivatedAbilityWithTapCost",
"hasActivatedAbility", "hasManaAbility",
"hasNonManaActivatedAbility", "NoAbilities", "HasCounters",
"wasNotCast", "ChosenType", "IsNotChosenType", "IsCommander");
private static final Set<String> VALID_EXCLUSIVE_STARTSWITH = ImmutableSortedSet
.of("named", "notnamed", "OwnedBy", "ControlledBy",
"ControllerControls", "AttachedTo", "EnchantedBy",
"NotEnchantedBy", "TopGraveyard", "SharesColorWith",
"MostProminentColor", "notSharesColorWith",
"sharesCreatureTypeWith", "sharesCardTypeWith",
"sharesNameWith", "doesNotShareNameWith",
"sharesControllerWith", "sharesOwnerWith",
"ThisTurnEntered", "ControlledByPlayerInTheDirection",
"sharesTypeWith", "hasKeyword", "with",
"greatestPowerControlledBy", "greatestCMCControlledBy",
"power", "toughness", "cmc", "totalPT", "counters", "non",
"RememberMap", "wasCastFrom", "wasNotCastFrom", "set",
"inZone", "HasSVar");
private static boolean isValidExclusive(final String valid) {
if (VALID_EXCLUSIVE.contains(valid)) {
return true;
}
if (Iterables.any(VALID_EXCLUSIVE_STARTSWITH, startsWith(valid))) {
return true;
}
return false;
}
private static final class KeyValuePair {
private final String key, value;
private final int index;
private KeyValuePair(final String key, final String value, final int index) {
this.key = key;
this.value = value;
this.index = index;
}
private String getKey() {
return key;
}
private String getValue() {
return value;
}
private int length() {
return keyLength() + 1 + valueLength();
}
private int keyLength() {
return key.length();
}
private int valueLength() {
return value.length();
}
private int startIndex() {
return index;
}
private int endIndexKey() {
return startIndex() + key.length();
}
private int startIndexValue() {
return endIndexKey() + 1;
}
private int endIndex() {
return startIndex() + length();
}
}
}

View File

@@ -5,6 +5,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.lang3.tuple.Pair;
@@ -50,24 +51,21 @@ import forge.game.player.PlayerView;
import forge.game.zone.PlayerZone;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.match.IMatchController;
import forge.match.MatchUtil;
import forge.match.input.ButtonUtil;
import forge.match.input.InputBase;
import forge.interfaces.IGuiGame;
import forge.model.FModel;
import forge.player.PlayerControllerHuman;
import forge.properties.ForgePreferences.FPref;
import forge.util.Lang;
import forge.util.gui.SGuiChoose;
import forge.util.maps.MapOfLists;
public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
private final PlayerControllerHuman humanController;
private final HashSet<CardView> cardsUpdate = new HashSet<CardView>();
private final HashSet<CardView> cardsRefreshDetails = new HashSet<CardView>();
private final HashSet<PlayerView> livesUpdate = new HashSet<PlayerView>();
private final HashSet<PlayerView> manaPoolUpdate = new HashSet<PlayerView>();
private final ArrayList<Pair<PlayerView, ZoneType>> zonesUpdate = new ArrayList<Pair<PlayerView, ZoneType>>();
private final IGuiGame matchController;
private final Set<CardView> cardsUpdate = new HashSet<CardView>();
private final Set<CardView> cardsRefreshDetails = new HashSet<CardView>();
private final Set<PlayerView> livesUpdate = new HashSet<PlayerView>();
private final Set<PlayerView> manaPoolUpdate = new HashSet<PlayerView>();
private final List<Pair<PlayerView, ZoneType>> zonesUpdate = new ArrayList<Pair<PlayerView, ZoneType>>();
private boolean processEventsQueued, needPhaseUpdate, needCombatUpdate, needStackUpdate, needPlayerControlUpdate;
private boolean gameOver, gameFinished;
@@ -75,6 +73,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public FControlGameEventHandler(final PlayerControllerHuman humanController0) {
humanController = humanController0;
matchController = humanController.getGui();
}
private final Runnable processEvents = new Runnable() {
@@ -82,68 +81,69 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
public void run() {
processEventsQueued = false;
IMatchController controller = MatchUtil.getController();
synchronized (cardsUpdate) {
if (!cardsUpdate.isEmpty()) {
MatchUtil.updateCards(cardsUpdate);
for (final CardView c : cardsUpdate) {
matchController.updateSingleCard(c);
}
cardsUpdate.clear();
}
}
synchronized (cardsRefreshDetails) {
if (!cardsRefreshDetails.isEmpty()) {
controller.refreshCardDetails(cardsRefreshDetails);
matchController.updateCards(cardsRefreshDetails);
cardsRefreshDetails.clear();
}
}
synchronized (livesUpdate) {
if (!livesUpdate.isEmpty()) {
controller.updateLives(livesUpdate);
matchController.updateLives(livesUpdate);
livesUpdate.clear();
}
}
synchronized (manaPoolUpdate) {
if (!manaPoolUpdate.isEmpty()) {
controller.updateManaPool(manaPoolUpdate);
matchController.updateManaPool(manaPoolUpdate);
manaPoolUpdate.clear();
}
}
if (turnUpdate != null) {
controller.updateTurn(turnUpdate);
matchController.updateTurn(turnUpdate);
turnUpdate = null;
}
if (needPhaseUpdate) {
needPhaseUpdate = false;
controller.updatePhase();
matchController.updatePhase();
}
if (needCombatUpdate) {
needCombatUpdate = false;
controller.showCombat();
matchController.showCombat();
}
if (needStackUpdate) {
needStackUpdate = false;
controller.updateStack();
matchController.updateStack();
}
if (needPlayerControlUpdate) {
needPlayerControlUpdate = false;
controller.updatePlayerControl();
matchController.updatePlayerControl();
}
synchronized (zonesUpdate) {
if (!zonesUpdate.isEmpty()) {
controller.updateZones(zonesUpdate);
matchController.updateZones(zonesUpdate);
zonesUpdate.clear();
}
}
if (gameOver) {
gameOver = false;
MatchUtil.onGameOver(true); // this will unlock any game threads waiting for inputs to complete
humanController.getInputQueue().onGameOver(true); // this will unlock any game threads waiting for inputs to complete
}
if (gameFinished) {
gameFinished = false;
PlayerView localPlayer = humanController.getLocalPlayerView();
InputBase.cancelAwaitNextInput(); //ensure "Waiting for opponent..." doesn't appear behind WinLo
controller.showPromptMessage(localPlayer, ""); //clear prompt behind WinLose overlay
ButtonUtil.update(localPlayer, "", "", false, false, false);
controller.finishGame();
final PlayerView localPlayer = humanController.getLocalPlayerView();
humanController.cancelAwaitNextInput(); //ensure "Waiting for opponent..." doesn't appear behind WinLo
matchController.showPromptMessage(localPlayer, ""); //clear prompt behind WinLose overlay
matchController.updateButtons(localPlayer, "", "", false, false, false);
matchController.finishGame();
humanController.updateAchievements();
}
}
@@ -161,13 +161,13 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
return null;
}
private Void processCard(Card card, HashSet<CardView> list) {
private Void processCard(Card card, Set<CardView> list) {
synchronized (list) {
list.add(card.getView());
}
return processEvent();
}
private Void processCards(Collection<Card> cards, HashSet<CardView> list) {
private Void processCards(Collection<Card> cards, Set<CardView> list) {
if (cards.isEmpty()) { return null; }
synchronized (list) {
@@ -177,7 +177,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
}
return processEvent();
}
private Void processPlayer(Player player, HashSet<PlayerView> list) {
private Void processPlayer(Player player, Set<PlayerView> list) {
synchronized (list) {
list.add(player.getView());
}
@@ -233,7 +233,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
options.add(fakeCard);
options.add(kv.getValue().getView());
}
SGuiChoose.reveal("These cards were chosen to ante", options);
humanController.getGui().reveal("These cards were chosen to ante", options);
return null;
}

View File

@@ -7,7 +7,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import com.google.common.eventbus.Subscribe;
import forge.FThreads;
import forge.GuiBase;
import forge.game.Game;
import forge.game.card.CardView;
import forge.game.event.GameEvent;
@@ -20,8 +19,6 @@ import forge.game.event.GameEventSpellAbilityCast;
import forge.game.event.GameEventSpellResolved;
import forge.game.event.GameEventTurnPhase;
import forge.game.event.IGameEventVisitor;
import forge.game.player.PlayerView;
import forge.match.MatchUtil;
import forge.match.input.InputPlaybackControl;
import forge.player.PlayerControllerHuman;
@@ -33,7 +30,15 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
private final PlayerControllerHuman humanController;
public FControlGamePlayback(final PlayerControllerHuman humanController0) {
humanController = humanController0;
this.humanController = humanController0;
}
public final PlayerControllerHuman getController() {
return humanController;
}
public InputPlaybackControl getInput() {
return inputPlayback;
}
private Game game;
@@ -42,7 +47,7 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
return game;
}
public void setGame(Game game0) {
public void setGame(final Game game0) {
game = game0;
inputPlayback = new InputPlaybackControl(game, this);
}
@@ -57,17 +62,17 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
private boolean fasterPlayback = false;
private void pauseForEvent(int delay) {
private void pauseForEvent(final int delay) {
try {
Thread.sleep(fasterPlayback ? delay / 10 : delay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
} catch (final InterruptedException e) {
// Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
e.printStackTrace();
}
}
@Override
public Void visit(GameEventBlockersDeclared event) {
public Void visit(final GameEventBlockersDeclared event) {
pauseForEvent(combatDelay);
return super.visit(event);
}
@@ -76,8 +81,9 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
* @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventTurnPhase)
*/
@Override
public Void visit(GameEventTurnPhase ev) {
boolean isUiToStop = MatchUtil.getController().stopAtPhase(PlayerView.get(ev.playerTurn), ev.phase);
public Void visit(final GameEventTurnPhase ev) {
try {
final boolean isUiToStop = !humanController.getGui().isUiSetToSkipPhase(ev.playerTurn.getView(), ev.phase);
switch(ev.phase) {
case COMBAT_END:
@@ -93,6 +99,9 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
}
break;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@@ -101,19 +110,19 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
* @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventDuelFinished)
*/
@Override
public Void visit(GameEventGameFinished event) {
public Void visit(final GameEventGameFinished event) {
humanController.getInputQueue().removeInput(inputPlayback);
return null;
}
@Override
public Void visit(GameEventGameStarted event) {
public Void visit(final GameEventGameStarted event) {
humanController.getInputQueue().setInput(inputPlayback);
return null;
}
@Override
public Void visit(GameEventLandPlayed event) {
public Void visit(final GameEventLandPlayed event) {
pauseForEvent(resolveDelay);
return super.visit(event);
}
@@ -123,7 +132,7 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
GuiBase.getInterface().setCard(CardView.get(event.spell.getHostCard()));
humanController.getGui().setCard(CardView.get(event.spell.getHostCard()));
}
});
pauseForEvent(resolveDelay);
@@ -138,7 +147,7 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
GuiBase.getInterface().setCard(CardView.get(event.sa.getHostCard()));
humanController.getGui().setCard(CardView.get(event.sa.getHostCard()));
}
});
pauseForEvent(castDelay);
@@ -149,17 +158,17 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
* @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventPlayerPriority)
*/
@Override
public Void visit(GameEventPlayerPriority event) {
public Void visit(final GameEventPlayerPriority event) {
inputPlayback.updateTurnMessage();
if (paused.get()) {
try {
gameThreadPauser.await();
gameThreadPauser.reset();
}
catch (InterruptedException e) {
catch (final InterruptedException e) {
e.printStackTrace();
}
catch (BrokenBarrierException e) {
catch (final BrokenBarrierException e) {
e.printStackTrace();
}
}
@@ -175,17 +184,16 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
private void releaseGameThread() {
// just need to run another thread through the barrier... not edt preferrably :)
getGame().getAction().invoke(new Runnable() {
@Override
public void run() {
try {
gameThreadPauser.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
} catch (final InterruptedException e) {
// Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
} catch (final BrokenBarrierException e) {
// Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
e.printStackTrace();
}
}
@@ -205,11 +213,7 @@ public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
releaseGameThread();
}
/**
* TODO: Write javadoc for this method.
* @param isFast
*/
public void setSpeed(boolean isFast) {
public void setSpeed(final boolean isFast) {
fasterPlayback = isFast;
}
}

View File

@@ -8,18 +8,19 @@ import java.util.List;
import forge.LobbyPlayer;
import forge.game.Game;
import forge.game.card.CardView;
import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.interfaces.IDevModeCheats;
import forge.interfaces.IGuiGame;
import forge.match.input.Input;
import forge.match.input.InputPlaybackControl;
import forge.player.PlayerControllerHuman;
import forge.util.ITriggerEvent;
public class WatchLocalGame extends PlayerControllerHuman {
public WatchLocalGame(Game game0, Player p, LobbyPlayer lp) {
super(game0, p, lp);
public WatchLocalGame(final Game game0, final LobbyPlayer lp, final IGuiGame gui) {
super(game0, null, lp);
setGui(gui);
}
@Override
@@ -77,11 +78,14 @@ public class WatchLocalGame extends PlayerControllerHuman {
}
@Override
public void selectPlayer(final PlayerView player, final ITriggerEvent triggerEvent) {
public void selectPlayer(final PlayerView player,
final ITriggerEvent triggerEvent) {
}
@Override
public boolean selectCard(final CardView card, final List<CardView> otherCardViewsToSelect, final ITriggerEvent triggerEvent) {
public boolean selectCard(final CardView card,
final List<CardView> otherCardViewsToSelect,
final ITriggerEvent triggerEvent) {
return false;
}
@@ -93,49 +97,13 @@ public class WatchLocalGame extends PlayerControllerHuman {
public void alphaStrike() {
}
@Override
public Iterable<String> getAutoYields() {
return null;
}
@Override
public boolean shouldAutoYield(final String key) {
return false;
}
@Override
public void setShouldAutoYield(final String key, final boolean autoYield) {
}
@Override
public boolean shouldAlwaysAcceptTrigger(final Integer trigger) {
return false;
}
@Override
public boolean shouldAlwaysDeclineTrigger(final Integer trigger) {
return false;
}
@Override
public boolean shouldAlwaysAskTrigger(final Integer trigger) {
return false;
}
@Override
public void setShouldAlwaysAcceptTrigger(final Integer trigger) {
}
@Override
public void setShouldAlwaysDeclineTrigger(final Integer trigger) {
}
@Override
public void setShouldAlwaysAskTrigger(final Integer trigger) {
}
@Override
public void autoPassCancel() {
}
@Override
public boolean canPlayUnlimitedLands() {
return false;
}
@Override
public DevModeCheats cheat() {
return null;
public IDevModeCheats cheat() {
return DevModeCheats.NO_CHEAT;
}
}

View File

@@ -3,4 +3,5 @@ package forge.events;
public interface IUiEventVisitor<T> {
T visit(UiEventBlockerAssigned event);
T visit(UiEventAttackerDeclared event);
T visit(UiEventNextGameDecision event);
}

View File

@@ -1,7 +1,8 @@
package forge.events;
import forge.game.event.Event;
public abstract class UiEvent {
public abstract class UiEvent extends Event {
public abstract <T> T visit(IUiEventVisitor<T> visitor);
}

View File

@@ -3,11 +3,10 @@ package forge.events;
import forge.game.GameEntityView;
import forge.game.card.CardView;
public class UiEventAttackerDeclared extends UiEvent {
public final CardView attacker;
public final GameEntityView defender;
public UiEventAttackerDeclared(final CardView card, final GameEntityView currentDefender) {
attacker = card;
defender = currentDefender;
@@ -17,7 +16,7 @@ public class UiEventAttackerDeclared extends UiEvent {
public <T> T visit(final IUiEventVisitor<T> visitor) {
return visitor.visit(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/

View File

@@ -0,0 +1,28 @@
package forge.events;
import forge.match.NextGameDecision;
import forge.player.PlayerControllerHuman;
public final class UiEventNextGameDecision extends UiEvent {
private final PlayerControllerHuman controller;
private final NextGameDecision decision;
public UiEventNextGameDecision(final PlayerControllerHuman controller, final NextGameDecision decision) {
this.controller = controller;
this.decision = decision;
}
public PlayerControllerHuman getController() {
return controller;
}
public NextGameDecision getDecision() {
return decision;
}
@Override
public <T> T visit(IUiEventVisitor<T> visitor) {
return visitor.visit(this);
}
}

View File

@@ -4,6 +4,7 @@ import java.util.List;
import com.google.common.collect.Lists;
import forge.GuiBase;
import forge.LobbyPlayer;
import forge.assets.FSkinProp;
import forge.deck.Deck;
@@ -12,13 +13,14 @@ import forge.game.GameView;
import forge.game.player.RegisteredPlayer;
import forge.interfaces.IButton;
import forge.interfaces.IWinLoseView;
import forge.match.MatchUtil;
import forge.match.HostedMatch;
import forge.model.FModel;
import forge.player.GamePlayerUtil;
public abstract class GauntletWinLoseController {
private final GameView lastGame;
private HostedMatch hostedMatch = null;
private final IWinLoseView<? extends IButton> view;
private final GameView lastGame;
public GauntletWinLoseController(IWinLoseView<? extends IButton> view0, final GameView game0) {
view = view0;
@@ -111,17 +113,21 @@ public abstract class GauntletWinLoseController {
public final boolean actionOnContinue() {
if (lastGame.isMatchOver()) {
// To change the AI deck, we have to create a new match.
GauntletData gd = FModel.getGauntletData();
Deck aiDeck = gd.getDecks().get(gd.getCompleted());
List<RegisteredPlayer> players = Lists.newArrayList();
players.add(new RegisteredPlayer(gd.getUserDeck()).setPlayer(GamePlayerUtil.getGuiPlayer()));
final GauntletData gd = FModel.getGauntletData();
final RegisteredPlayer human = new RegisteredPlayer(gd.getUserDeck()).setPlayer(GamePlayerUtil.getGuiPlayer());
final Deck aiDeck = gd.getDecks().get(gd.getCompleted());
final List<RegisteredPlayer> players = Lists.newArrayList();
players.add(human);
players.add(new RegisteredPlayer(aiDeck).setPlayer(GamePlayerUtil.createAiPlayer()));
view.hide();
saveOptions();
MatchUtil.endCurrentGame();
if (hostedMatch != null) {
hostedMatch.endCurrentGame();
}
MatchUtil.startMatch(GameType.Gauntlet, players);
hostedMatch = GuiBase.getInterface().hostMatch();
hostedMatch.startMatch(GameType.Gauntlet, null, players, human, GuiBase.getInterface().getNewGuiGame());
return true;
}
return false;

View File

@@ -0,0 +1,87 @@
package forge.interfaces;
public interface IDevModeCheats {
void setCanPlayUnlimitedLands(boolean canPlayUnlimitedLands0);
void setViewAllCards(boolean canViewAll);
void generateMana();
void dumpGameState();
void setupGameState();
void tutorForCard();
void addCountersToPermanent();
void tapPermanents();
void untapPermanents();
void setPlayerLife();
void winGame();
void addCardToHand();
void addCardToBattlefield();
void riggedPlanarRoll();
void planeswalkTo();
/**
* Implementation of {@link IDevModeCheats} that disallows cheating by
* performing no action whatsoever when any of its methods is called.
*/
public static final IDevModeCheats NO_CHEAT = new IDevModeCheats() {
@Override
public void winGame() {
}
@Override
public void untapPermanents() {
}
@Override
public void tutorForCard() {
}
@Override
public void tapPermanents() {
}
@Override
public void setupGameState() {
}
@Override
public void setViewAllCards(final boolean canViewAll) {
}
@Override
public void setPlayerLife() {
}
@Override
public void setCanPlayUnlimitedLands(final boolean canPlayUnlimitedLands0) {
}
@Override
public void riggedPlanarRoll() {
}
@Override
public void planeswalkTo() {
}
@Override
public void generateMana() {
}
@Override
public void dumpGameState() {
}
@Override
public void addCountersToPermanent() {
}
@Override
public void addCardToHand() {
}
@Override
public void addCardToBattlefield() {
}
};
}

View File

@@ -0,0 +1,45 @@
package forge.interfaces;
import java.util.List;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.match.NextGameDecision;
import forge.util.ITriggerEvent;
public interface IGameController {
boolean mayLookAtAllCards();
boolean canPlayUnlimitedLands();
void concede();
void alphaStrike();
boolean useMana(byte color);
void selectButtonOk();
void selectButtonCancel();
boolean passPriority();
boolean passPriorityUntilEndOfTurn();
void selectPlayer(PlayerView playerView, ITriggerEvent triggerEvent);
boolean selectCard(CardView cardView,
List<CardView> otherCardViewsToSelect, ITriggerEvent triggerEvent);
void selectAbility(SpellAbility sa);
boolean tryUndoLastAction();
IDevModeCheats cheat();
void nextGameDecision(NextGameDecision decision);
String getActivateDescription(CardView card);
}

View File

@@ -1,34 +1,28 @@
package forge.interfaces;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.List;
import com.google.common.base.Function;
import forge.LobbyPlayer;
import forge.assets.FSkinProp;
import forge.assets.ISkinImage;
import forge.deck.CardPool;
import forge.download.GuiDownloadService;
import forge.game.GameEntity;
import forge.game.GameEntityView;
import forge.game.card.CardView;
import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon;
import forge.item.PaperCard;
import forge.player.PlayerControllerHuman;
import forge.match.HostedMatch;
import forge.sound.IAudioClip;
import forge.sound.IAudioMusic;
import forge.util.Callback;
import forge.util.FCollectionView;
public interface IGuiBase {
boolean isRunningOnDesktop();
String getCurrentVersion();
String getAssetsDir();
void invokeInEdtLater(Runnable runnable);
void invokeInEdtAndWait(final Runnable proc);
void invokeInEdtAndWait(Runnable proc);
boolean isGuiThread();
IGuiTimer createGuiTimer(Runnable proc, int interval);
ISkinImage getSkinIcon(FSkinProp skinProp);
@@ -38,27 +32,23 @@ public interface IGuiBase {
void showBugReportDialog(String title, String text, boolean showExitAppBtn);
void showImageDialog(ISkinImage image, String message, String title);
int showOptionDialog(String message, String title, FSkinProp icon, String[] options, int defaultOption);
int showCardOptionDialog(CardView card, String message, String title, FSkinProp icon, String[] options, int defaultOption);
String showInputDialog(String message, String title, FSkinProp icon, String initialInput, String[] inputOptions);
<T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices, final T selected, final Function<T, String> display);
<T> List<T> order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax,
final List<T> sourceChoices, final List<T> destChoices, final CardView referenceCard, final boolean sideboardingMode);
List<PaperCard> sideboard(CardPool sideboard, CardPool main);
GameEntityView chooseSingleEntityForEffect(String title, FCollectionView<? extends GameEntity> optionList, DelayedReveal delayedReveal, boolean isOptional, PlayerControllerHuman controller);
<T> List<T> getChoices(String message, int min, int max, Collection<T> choices, T selected, Function<T, String> display);
<T> List<T> order(String title, String top, int remainingObjectsMin, int remainingObjectsMax, List<T> sourceChoices, List<T> destChoices);
String showFileDialog(String title, String defaultDir);
File getSaveFile(File defaultFile);
void download(GuiDownloadService service, Callback<Boolean> callback);
void showCardList(final String title, final String message, final List<PaperCard> list);
boolean showBoxedProduct(final String title, final String message, final List<PaperCard> list);
void setCard(CardView card);
void showCardList(String title, String message, List<PaperCard> list);
boolean showBoxedProduct(String title, String message, List<PaperCard> list);
int getAvatarCount();
void copyToClipboard(String text);
void browseToUrl(String url) throws Exception;
void browseToUrl(String url) throws IOException, URISyntaxException;
IAudioClip createAudioClip(String filename);
IAudioMusic createAudioMusic(String filename);
void startAltSoundSystem(String filename, boolean isSynchronized);
void clearImageCache();
void showSpellShop();
void showBazaar();
void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi);
IGuiGame getNewGuiGame();
HostedMatch hostMatch();
}

View File

@@ -0,0 +1,196 @@
package forge.interfaces;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import forge.LobbyPlayer;
import forge.assets.FSkinProp;
import forge.deck.CardPool;
import forge.game.GameEntity;
import forge.game.GameEntityView;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
import forge.game.player.IHasIcon;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.item.PaperCard;
import forge.player.PlayerControllerHuman;
import forge.util.FCollectionView;
import forge.util.ITriggerEvent;
public interface IGuiGame {
void setGameView(GameView gameView);
void setGameController(PlayerView player, IGameController gameController);
boolean resetForNewGame();
void openView(FCollectionView<PlayerView> myPlayers);
void afterGameEnd();
void showCombat();
void showPromptMessage(PlayerView playerView, String message);
boolean stopAtPhase(PlayerView playerTurn, PhaseType phase);
IButton getBtnOK(PlayerView playerView);
IButton getBtnCancel(PlayerView playerView);
void focusButton(IButton button);
void flashIncorrectAction();
void updatePhase();
void updateTurn(PlayerView player);
void updatePlayerControl();
void enableOverlay();
void disableOverlay();
void finishGame();
Object showManaPool(PlayerView player);
void hideManaPool(PlayerView player, Object zoneToRestore);
void updateStack();
void updateZones(List<Pair<PlayerView, ZoneType>> zonesToUpdate);
void updateSingleCard(CardView card);
void updateCards(Iterable<CardView> cards);
void updateManaPool(Iterable<PlayerView> manaPoolUpdate);
void updateLives(Iterable<PlayerView> livesUpdate);
void setPanelSelection(CardView hostCard);
void hear(LobbyPlayer player, String message);
SpellAbility getAbilityToPlay(List<SpellAbility> abilities,
ITriggerEvent triggerEvent);
Map<CardView, Integer> assignDamage(CardView attacker,
List<CardView> blockers, int damage, GameEntityView defender,
boolean overrideOrder);
void message(String message);
void message(String message, String title);
void showErrorDialog(String message);
void showErrorDialog(String message, String title);
boolean showConfirmDialog(String message);
boolean showConfirmDialog(String message, String title);
boolean showConfirmDialog(String message, String title, boolean defaultYes);
boolean showConfirmDialog(String message, String title, String yesButtonText, String noButtonText);
boolean showConfirmDialog(String message, String title, String yesButtonText, String noButtonText, boolean defaultYes);
int showOptionDialog(String message, String title, FSkinProp icon,
String[] options, int defaultOption);
int showCardOptionDialog(CardView card, String message, String title,
FSkinProp icon, String[] options, int defaultOption);
String showInputDialog(String message, String title);
String showInputDialog(String message, String title, FSkinProp icon);
String showInputDialog(String message, String title, FSkinProp icon,
String initialInput);
String showInputDialog(String message, String title, FSkinProp icon,
String initialInput, String[] inputOptions);
boolean confirm(CardView c, String question);
boolean confirm(CardView c, String question, boolean defaultChoice);
boolean confirm(CardView c, String question, String[] options);
boolean confirm(CardView c, String question, boolean defaultIsYes, String[] options);
<T> List<T> getChoices(String message, int min, int max, T[] choices);
<T> List<T> getChoices(String message, int min, int max,
Collection<T> choices);
<T> List<T> getChoices(String message, int min, int max,
Collection<T> choices, T selected, Function<T, String> display);
// Get Integer in range
Integer getInteger(String message);
Integer getInteger(String message, int min);
Integer getInteger(String message, int min, int max);
Integer getInteger(String message, int min, int max, boolean sortDesc);
Integer getInteger(String message, int min, int max, int cutoff);
/**
* Convenience for getChoices(message, 0, 1, choices).
*
* @param <T>
* is automatically inferred.
* @param message
* a {@link java.lang.String} object.
* @param choices
* a T object.
* @return null if choices is missing, empty, or if the users' choices are
* empty; otherwise, returns the first item in the List returned by
* getChoices.
* @see #getChoices(String, int, int, Object...)
*/
<T> T oneOrNone(String message, T[] choices);
<T> T oneOrNone(String message, Collection<T> choices);
// returned Object will never be null
/**
* <p>
* getChoice.
* </p>
*
* @param <T>
* a T object.
* @param message
* a {@link java.lang.String} object.
* @param choices
* a T object.
* @return a T object.
*/
<T> T one(String message, T[] choices);
<T> T one(String message, Collection<T> choices);
<T> List<T> noneOrMany(String message, Collection<T> choices);
<T> void reveal(String message, T item);
<T> void reveal(String message, T[] items);
<T> void reveal(String message, Collection<T> items);
<T> List<T> many(String title, String topCaption, int min, int max,
List<T> sourceChoices);
<T> List<T> many(String title, String topCaption, int cnt,
List<T> sourceChoices);
<T> List<T> many(String title, String topCaption, int cnt,
List<T> sourceChoices, CardView c);
<T> List<T> many(String title, String topCaption, int min, int max,
List<T> sourceChoices, CardView c);
<T> List<T> order(String title, String top, List<T> sourceChoices);
<T> List<T> order(String title, String top, List<T> sourceChoices, CardView c);
<T> List<T> order(String title, String top, int remainingObjectsMin,
int remainingObjectsMax, List<T> sourceChoices,
List<T> destChoices, CardView referenceCard,
boolean sideboardingMode);
<T> List<T> insertInList(String title, T newItem, List<T> oldItems);
List<PaperCard> sideboard(CardPool sideboard, CardPool main);
GameEntityView chooseSingleEntityForEffect(String title,
FCollectionView<? extends GameEntity> optionList,
DelayedReveal delayedReveal, boolean isOptional,
PlayerControllerHuman controller);
void setCard(CardView card);
void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi);
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);
void restoreOldZones(Map<PlayerView, Object> playersToRestoreZonesFor);
void updateButtons(PlayerView owner, boolean okEnabled,
boolean cancelEnabled, boolean focusOk);
void updateButtons(PlayerView owner, String okLabel, String cancelLabel,
boolean okEnabled, boolean cancelEnabled, boolean focusOk);
void setHighlighted(PlayerView pv, boolean b);
void setUsedToPay(CardView card, boolean value);
void awaitNextInput();
void cancelAwaitNextInput();
boolean isUiSetToSkipPhase(PlayerView playerTurn, PhaseType phase);
void autoPassUntilEndOfTurn(PlayerView player);
boolean mayAutoPass(PlayerView player);
void autoPassCancel(PlayerView player);
void updateAutoPassPrompt();
boolean shouldAutoYield(String key);
void setShouldAutoYield(String key, boolean autoYield);
boolean shouldAlwaysAcceptTrigger(int trigger);
boolean shouldAlwaysDeclineTrigger(int trigger);
boolean shouldAlwaysAskTrigger(int trigger);
void setShouldAlwaysAcceptTrigger(int trigger);
void setShouldAlwaysDeclineTrigger(int trigger);
void setShouldAlwaysAskTrigger(int trigger);
void setCurrentPlayer(PlayerView player);
}

View File

@@ -0,0 +1,36 @@
package forge.interfaces;
import forge.game.card.CardView;
/**
* Interface that receives requests on whether a {@link Card} can be shown.
*/
public interface IMayViewCards {
/**
* @param c
* a {@link CardView}
* @return whether {@code c} can be shown.
*/
boolean mayView(CardView c);
/**
* @param c
* a {@link CardView}
* @return whether the flip side of {@code c} can be shown.
*/
boolean mayFlip(CardView c);
/** {@link IMayViewCards} that lets you view all cards unconditionally. */
public static final IMayViewCards ALL = new IMayViewCards() {
@Override
public boolean mayView(final CardView c) {
return true;
}
@Override
public boolean mayFlip(final CardView c) {
return c.hasAlternateState();
}
};
}

View File

@@ -20,10 +20,11 @@ package forge.limited;
import java.util.ArrayList;
import java.util.List;
import forge.GuiBase;
import forge.deck.Deck;
import forge.game.GameType;
import forge.game.player.RegisteredPlayer;
import forge.match.MatchUtil;
import forge.match.HostedMatch;
import forge.model.FModel;
import forge.player.GamePlayerUtil;
import forge.util.Aggregates;
@@ -38,6 +39,7 @@ import forge.util.Aggregates;
* @since 1.2.xx
*/
public class GauntletMini {
private HostedMatch hostedMatch = null;
private int rounds;
private Deck humanDeck;
private int currentRound;
@@ -76,14 +78,17 @@ public class GauntletMini {
* Advances the tournament to the next round.
*/
public void nextRound() {
// System.out.println("Moving from round " + currentRound + " to round " + currentRound + 1 + " of " + rounds);
if (hostedMatch == null) {
return;
}
if (currentRound >= rounds) {
currentRound = rounds - 1;
return;
}
currentRound++;
MatchUtil.endCurrentGame();
hostedMatch.endCurrentGame();
startRound();
}
@@ -129,18 +134,19 @@ public class GauntletMini {
}
resetCurrentRound();
startRound();
}
/**
* Starts the tournament.
*/
private void startRound() {
List<RegisteredPlayer> starter = new ArrayList<RegisteredPlayer>();
starter.add(new RegisteredPlayer(humanDeck).setPlayer(GamePlayerUtil.getGuiPlayer()));
final List<RegisteredPlayer> starter = new ArrayList<RegisteredPlayer>();
final RegisteredPlayer human = new RegisteredPlayer(humanDeck).setPlayer(GamePlayerUtil.getGuiPlayer());
starter.add(human);
starter.add(aiOpponents.get(currentRound - 1).setPlayer(GamePlayerUtil.createAiPlayer()));
MatchUtil.startMatch(gauntletType, starter);
final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch();
hostedMatch.startMatch(gauntletType, null, starter, human, GuiBase.getInterface().getNewGuiGame());
}
/**

View File

@@ -0,0 +1,648 @@
package forge.match;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import forge.FThreads;
import forge.assets.FSkinProp;
import forge.card.CardStateName;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.card.CardView.CardStateView;
import forge.game.player.PlayerView;
import forge.interfaces.IButton;
import forge.interfaces.IGameController;
import forge.interfaces.IGuiGame;
import forge.interfaces.IMayViewCards;
import forge.util.FCollectionView;
public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
private FCollectionView<PlayerView> localPlayers;
private PlayerView currentPlayer = null;
protected final void setLocalPlayers(final FCollectionView<PlayerView> players) {
this.localPlayers = players;
}
public final boolean hasLocalPlayers() {
return localPlayers != null && !localPlayers.isEmpty();
}
public final FCollectionView<PlayerView> getLocalPlayers() {
return localPlayers;
}
public final int getLocalPlayerCount() {
return localPlayers == null ? 0 : localPlayers.size();
}
public final boolean isLocalPlayer(final PlayerView player) {
return hasLocalPlayers() && localPlayers.contains(player);
}
public final PlayerView getCurrentPlayer() {
return currentPlayer;
}
@Override
public final void setCurrentPlayer(final PlayerView player) {
this.currentPlayer = player;
updateCurrentPlayer(player);
}
protected abstract void updateCurrentPlayer(PlayerView player);
private GameView gameView = null;
public final GameView getGameView() {
return gameView;
}
public void setGameView(final GameView gameView) {
this.gameView = gameView;
}
private final Map<PlayerView, IGameController> gameControllers = Maps.newHashMap();
public final IGameController getGameController() {
return gameControllers.get(currentPlayer);
}
public final Collection<IGameController> getGameControllers() {
return gameControllers.values();
}
@Override
public void setGameController(final PlayerView player, final IGameController gameController) {
this.gameControllers.put(player, gameController);
}
@Override
public void updateCards(final Iterable<CardView> cards) {
for (final CardView card : cards) {
updateSingleCard(card);
}
}
public String getCardImageKey(final CardStateView csv) {
if (getCurrentPlayer() == null) { return csv.getImageKey(null); } //if not in game, card can be shown
return csv.getImageKey(getCurrentPlayer());
}
@Override
public boolean mayView(final CardView c) {
if (!hasLocalPlayers()) {
return true; //if not in game, card can be shown
}
if (getGameController().mayLookAtAllCards()) {
return true;
}
return Iterables.any(localPlayers, new Predicate<PlayerView>() {
@Override public boolean apply(final PlayerView input) { return c.canBeShownTo(input); };
});
}
@Override
public boolean mayFlip(final CardView cv) {
if (cv == null) { return false; }
CardStateView altState = cv.getAlternateState();
if (altState == null) { return false; }
switch (altState.getState()) {
case Original:
CardStateView currentState = cv.getCurrentState();
if (currentState.getState() == CardStateName.FaceDown) {
return getCurrentPlayer() == null || cv.canFaceDownBeShownTo(getCurrentPlayer());
}
return true; //original can always be shown if not a face down that can't be shown
case Flipped:
case Transformed:
return true;
default:
return false;
}
}
private Set<PlayerView> highlightedPlayers = Sets.newHashSet();
@Override
public void setHighlighted(final PlayerView pv, final boolean b) {
if (b) {
highlightedPlayers.add(pv);
} else {
highlightedPlayers.remove(pv);
}
}
public boolean isHighlighted(PlayerView player) {
return highlightedPlayers.contains(player);
}
private Set<CardView> highlightedCards = Sets.newHashSet();
// used to highlight cards in UI
@Override
public void setUsedToPay(CardView card, boolean value) {
boolean hasChanged = value ? highlightedCards.add(card) : highlightedCards.remove(card);
if (hasChanged) { // since we are in UI thread, may redraw the card right now
updateSingleCard(card);
}
}
public boolean isUsedToPay(CardView card) {
return highlightedCards.contains(card);
}
/** Concede game, bring up WinLose UI. */
public void concede() {
final String userPrompt =
"This will end the current game and you will not be able to resume.\n\n" +
"Concede anyway?";
if (hasLocalPlayers() && showConfirmDialog(userPrompt, "Concede Game?", "Concede", "Cancel")) {
for (final IGameController c : getGameControllers()) {
// Concede each player on this Gui
c.concede();
}
/*
if (humanCount == 0) { // no human? then all players surrender!
for (Player p : game.getPlayers()) {
p.concede();
}
}
else {
getCurrentPlayer().concede();
}
Player priorityPlayer = game.getPhaseHandler().getPriorityPlayer();
boolean humanHasPriority = priorityPlayer == null || priorityPlayer.getLobbyPlayer() instanceof LobbyPlayerHuman;
if (humanCount > 0 && humanHasPriority) {
game.getAction().checkGameOverCondition();
}
else {
game.isGameOver(); // this is synchronized method - it's used to make Game-0 thread see changes made here
onGameOver(false); //release any waiting input, effectively passing priority
}
if (playbackControl != null) {
playbackControl.onGameStopRequested();
}
*/
}
}
@Override
public void updateButtons(final PlayerView owner, final boolean okEnabled, final boolean cancelEnabled, final boolean focusOk) {
updateButtons(owner, "OK", "Cancel", okEnabled, cancelEnabled, focusOk);
}
@Override
public void updateButtons(final PlayerView owner, final String okLabel, final String cancelLabel, final boolean okEnabled, final boolean cancelEnabled, final boolean focusOk) {
final IButton btnOk = getBtnOK(owner);
final IButton btnCancel = getBtnCancel(owner);
btnOk.setText(okLabel);
btnCancel.setText(cancelLabel);
btnOk.setEnabled(okEnabled);
btnCancel.setEnabled(cancelEnabled);
if (okEnabled && focusOk) {
focusButton(btnOk);
} else if (cancelEnabled) {
focusButton(btnCancel);
}
}
// Auto-yield and other input-related code
private final Set<PlayerView> autoPassUntilEndOfTurn = Sets.newHashSet();
/**
* Automatically pass priority until reaching the Cleanup phase of the
* current turn.
*/
@Override
public final void autoPassUntilEndOfTurn(final PlayerView player) {
autoPassUntilEndOfTurn.add(player);
updateAutoPassPrompt();
}
public final void autoPassCancel(final PlayerView player) {
if (!autoPassUntilEndOfTurn.remove(player)) {
return;
}
//prevent prompt getting stuck on yielding message while actually waiting for next input opportunity
final PlayerView playerView = getCurrentPlayer();
showPromptMessage(playerView, "");
updateButtons(playerView, false, false, false);
awaitNextInput();
}
public final boolean mayAutoPass(final PlayerView player) {
return autoPassUntilEndOfTurn.contains(player);
}
private final Timer awaitNextInputTimer = new Timer();
private TimerTask awaitNextInputTask;
@Override
public final void awaitNextInput() {
//delay updating prompt to await next input briefly so buttons don't flicker disabled then enabled
awaitNextInputTask = new TimerTask() {
@Override
public void run() {
FThreads.invokeInEdtLater(new Runnable() {
@Override
public void run() {
synchronized (awaitNextInputTimer) {
if (awaitNextInputTask != null) {
updatePromptForAwait(getCurrentPlayer());
awaitNextInputTask = null;
}
}
}
});
}
};
awaitNextInputTimer.schedule(awaitNextInputTask, 250);
}
protected final void updatePromptForAwait(final PlayerView playerView) {
showPromptMessage(playerView, "Waiting for opponent...");
updateButtons(playerView, false, false, false);
}
@Override
public final void cancelAwaitNextInput() {
synchronized (awaitNextInputTimer) { //ensure task doesn't reset awaitNextInputTask during this block
if (awaitNextInputTask != null) {
try {
awaitNextInputTask.cancel(); //cancel timer once next input shown if needed
} catch (final Exception ex) {} //suppress any exception thrown by cancel()
awaitNextInputTask = null;
}
}
}
@Override
public final void updateAutoPassPrompt() {
if (!autoPassUntilEndOfTurn.isEmpty()) {
//allow user to cancel auto-pass
cancelAwaitNextInput(); //don't overwrite prompt with awaiting opponent
showPromptMessage(getCurrentPlayer(), "Yielding until end of turn.\nYou may cancel this yield to take an action.");
updateButtons(getCurrentPlayer(), false, true, false);
}
}
// End auto-yield/input code
// Abilities to auto-yield to
private final Set<String> autoYields = Sets.newHashSet();
public final Iterable<String> getAutoYields() {
return autoYields;
}
@Override
public final boolean shouldAutoYield(final String key) {
return !getDisableAutoYields() && autoYields.contains(key);
}
@Override
public final void setShouldAutoYield(final String key, final boolean autoYield) {
if (autoYield) {
autoYields.add(key);
}
else {
autoYields.remove(key);
}
}
private boolean disableAutoYields;
public final boolean getDisableAutoYields() {
return disableAutoYields;
}
public final void setDisableAutoYields(final boolean b0) {
disableAutoYields = b0;
}
// Triggers preliminary choice: ask, decline or play
private final Map<Integer, Boolean> triggersAlwaysAccept = Maps.newTreeMap();
@Override
public final boolean shouldAlwaysAcceptTrigger(final int trigger) { return Boolean.TRUE.equals(triggersAlwaysAccept.get(Integer.valueOf(trigger))); }
@Override
public final boolean shouldAlwaysDeclineTrigger(final int trigger) { return Boolean.FALSE.equals(triggersAlwaysAccept.get(Integer.valueOf(trigger))); }
@Override
public final boolean shouldAlwaysAskTrigger(final int trigger) { return !triggersAlwaysAccept.containsKey(Integer.valueOf(trigger)); }
@Override
public final void setShouldAlwaysAcceptTrigger(final int trigger) { triggersAlwaysAccept.put(Integer.valueOf(trigger), Boolean.TRUE); }
@Override
public final void setShouldAlwaysDeclineTrigger(final int trigger) { triggersAlwaysAccept.put(Integer.valueOf(trigger), Boolean.FALSE); }
@Override
public final void setShouldAlwaysAskTrigger(final int trigger) { triggersAlwaysAccept.remove(Integer.valueOf(trigger)); }
// End of Triggers preliminary choice
// Start of Choice code
/**
* Convenience for getChoices(message, 0, 1, choices).
*
* @param <T>
* is automatically inferred.
* @param message
* a {@link java.lang.String} object.
* @param choices
* a T object.
* @return null if choices is missing, empty, or if the users' choices are
* empty; otherwise, returns the first item in the List returned by
* getChoices.
* @see #getChoices(String, int, int, Object...)
*/
@Override
public <T> T oneOrNone(final String message, final T[] choices) {
if ((choices == null) || (choices.length == 0)) {
return null;
}
final List<T> choice = getChoices(message, 0, 1, choices);
return choice.isEmpty() ? null : choice.get(0);
}
@Override
public <T> T oneOrNone(final String message, final Collection<T> choices) {
if ((choices == null) || choices.isEmpty()) {
return null;
}
final List<T> choice = getChoices(message, 0, 1, choices);
return choice.isEmpty() ? null : choice.get(0);
}
// returned Object will never be null
/**
* <p>
* getChoice.
* </p>
*
* @param <T>
* a T object.
* @param message
* a {@link java.lang.String} object.
* @param choices
* a T object.
* @return a T object.
*/
@Override
public <T> T one(final String message, final T[] choices) {
final List<T> choice = getChoices(message, 1, 1, choices);
assert choice.size() == 1;
return choice.get(0);
}
@Override
public <T> T one(final String message, final Collection<T> choices) {
if (choices == null || choices.isEmpty()) {
return null;
}
if (choices.size() == 1) {
return Iterables.getFirst(choices, null);
}
final List<T> choice = getChoices(message, 1, 1, choices);
assert choice.size() == 1;
return choice.get(0);
}
@Override
public <T> List<T> noneOrMany(final String message, final Collection<T> choices) {
return getChoices(message, 0, choices.size(), choices, null, null);
}
@Override
// Nothing to choose here. Code uses this to just reveal one or more items
public <T> void reveal(final String message, final T item) {
List<T> items = new ArrayList<T>();
items.add(item);
reveal(message, items);
}
@Override
public <T> void reveal(final String message, final T[] items) {
getChoices(message, -1, -1, items);
}
@Override
public <T> void reveal(final String message, final Collection<T> items) {
getChoices(message, -1, -1, items);
}
// Get Integer in range
@Override
public Integer getInteger(final String message) {
return getInteger(message, 0, Integer.MAX_VALUE, false);
}
@Override
public Integer getInteger(final String message, int min) {
return getInteger(message, min, Integer.MAX_VALUE, false);
}
@Override
public Integer getInteger(final String message, int min, int max) {
return getInteger(message, min, max, false);
}
@Override
public Integer getInteger(final String message, final int min, final int max, final boolean sortDesc) {
if (max <= min) { return min; } //just return min if max <= min
//force cutting off after 100 numbers at most
if (max == Integer.MAX_VALUE) {
return getInteger(message, min, max, min + 99);
}
int count = max - min + 1;
if (count > 100) {
return getInteger(message, min, max, min + 99);
}
final Integer[] choices = new Integer[count];
if (sortDesc) {
for (int i = 0; i < count; i++) {
choices[count - i - 1] = Integer.valueOf(i + min);
}
}
else {
for (int i = 0; i < count; i++) {
choices[i] = Integer.valueOf(i + min);
}
}
return oneOrNone(message, choices);
}
@Override
public Integer getInteger(final String message, final int min, final int max, final int cutoff) {
if (max <= min || cutoff < min) {
return min; //just return min if max <= min or cutoff < min
}
if (cutoff >= max) { //fallback to regular integer prompt if cutoff at or after max
return getInteger(message, min, max);
}
final List<Object> choices = new ArrayList<Object>();
for (int i = min; i <= cutoff; i++) {
choices.add(Integer.valueOf(i));
}
choices.add("Other...");
final Object choice = oneOrNone(message, choices);
if (choice instanceof Integer || choice == null) {
return (Integer)choice;
}
//if Other option picked, prompt for number input
String prompt = "Enter a number";
if (min != Integer.MIN_VALUE) {
if (max != Integer.MAX_VALUE) {
prompt += " between " + min + " and " + max;
} else {
prompt += " greater than or equal to " + min;
}
} else if (max != Integer.MAX_VALUE) {
prompt += " less than or equal to " + max;
}
prompt += ":";
while (true) {
final String str = showInputDialog(prompt, message);
if (str == null) { return null; } // that is 'cancel'
if (StringUtils.isNumeric(str)) {
final Integer val = Integer.valueOf(str);
if (val >= min && val <= max) {
return val;
}
}
}
}
// returned Object will never be null
@Override
public <T> List<T> getChoices(final String message, final int min, final int max, final T[] choices) {
return getChoices(message, min, max, Arrays.asList(choices), null, null);
}
@Override
public <T> List<T> getChoices(final String message, final int min, final int max, final Collection<T> choices) {
return getChoices(message, min, max, choices, null, null);
}
@Override
public <T> List<T> many(final String title, final String topCaption, int cnt, final List<T> sourceChoices) {
return many(title, topCaption, cnt, sourceChoices, null);
}
@Override
public <T> List<T> many(final String title, final String topCaption, final int cnt, final List<T> sourceChoices, final CardView c) {
return many(title, topCaption, cnt, cnt, sourceChoices, c);
}
@Override
public <T> List<T> many(final String title, final String topCaption, final int min, final int max, final List<T> sourceChoices) {
return many(title, topCaption, min, max, sourceChoices, null);
}
@Override
public <T> List<T> many(final String title, final String topCaption, int min, int max, final List<T> sourceChoices, final CardView c) {
final int m2 = min >= 0 ? sourceChoices.size() - min : -1;
final int m1 = max >= 0 ? sourceChoices.size() - max : -1;
return order(title, topCaption, m1, m2, sourceChoices, null, c, false);
}
@Override
public <T> List<T> order(final String title, final String top, final List<T> sourceChoices) {
return order(title, top, sourceChoices, null);
}
@Override
public <T> List<T> order(final String title, final String top, final List<T> sourceChoices, final CardView c) {
return order(title, top, 0, 0, sourceChoices, null, c, false);
}
/**
* Ask the user to insert an object into a list of other objects. The
* current implementation requires the user to cancel in order to get the
* new item to be the first item in the resulting list.
*
* @param title the dialog title.
* @param newItem the object to insert.
* @param oldItems the list of objects.
* @return A shallow copy of the list of objects, with newItem inserted.
*/
@Override
public <T> List<T> insertInList(final String title, final T newItem, final List<T> oldItems) {
final T placeAfter = oneOrNone(title, oldItems);
final int indexAfter = (placeAfter == null ? 0 : oldItems.indexOf(placeAfter) + 1);
final List<T> result = Lists.newArrayListWithCapacity(oldItems.size() + 1);
result.addAll(oldItems);
result.add(indexAfter, newItem);
return result;
}
@Override
public String showInputDialog(final String message, final String title) {
return showInputDialog(message, title, null, "", null);
}
@Override
public String showInputDialog(final String message, final String title, final FSkinProp icon) {
return showInputDialog(message, title, icon, "", null);
}
@Override
public String showInputDialog(final String message, final String title, final FSkinProp icon, final String initialInput) {
return showInputDialog(message, title, icon, initialInput, null);
}
@Override
public boolean confirm(final CardView c, final String question) {
return confirm(c, question, true, null);
}
@Override
public boolean confirm(final CardView c, final String question, final boolean defaultChoice) {
return confirm(c, question, defaultChoice, null);
}
@Override
public boolean confirm(final CardView c, final String question, String[] options) {
return confirm(c, question, true, options);
}
@Override
public void message(final String message) {
message(message, "Forge");
}
@Override
public void showErrorDialog(final String message) {
showErrorDialog(message, "Error");
}
@Override
public boolean showConfirmDialog(final String message) {
return showConfirmDialog(message, null);
}
@Override
public boolean showConfirmDialog(final String message, final String title) {
return showConfirmDialog(message, title, true);
}
@Override
public boolean showConfirmDialog(final String message, final String title,
final boolean defaultYes) {
return showConfirmDialog(message, title, "Yes", "No");
}
@Override
public boolean showConfirmDialog(final String message, final String title,
final String yesButtonText, final String noButtonText) {
return showConfirmDialog(message, title, yesButtonText, noButtonText, true);
}
// End of Choice code
}

View File

@@ -0,0 +1,358 @@
package forge.match;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.eventbus.Subscribe;
import forge.FThreads;
import forge.GuiBase;
import forge.LobbyPlayer;
import forge.control.FControlGameEventHandler;
import forge.control.FControlGamePlayback;
import forge.control.WatchLocalGame;
import forge.events.IUiEventVisitor;
import forge.events.UiEvent;
import forge.events.UiEventAttackerDeclared;
import forge.events.UiEventBlockerAssigned;
import forge.events.UiEventNextGameDecision;
import forge.game.Game;
import forge.game.GameRules;
import forge.game.GameType;
import forge.game.GameView;
import forge.game.Match;
import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.player.RegisteredPlayer;
import forge.interfaces.IGuiGame;
import forge.model.FModel;
import forge.player.GamePlayerUtil;
import forge.player.LobbyPlayerHuman;
import forge.player.PlayerControllerHuman;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.quest.QuestController;
import forge.sound.MusicPlaylist;
import forge.sound.SoundSystem;
import forge.util.CollectionSuppliers;
import forge.util.FCollection;
import forge.util.GuiDisplayUtil;
import forge.util.NameGenerator;
import forge.util.maps.HashMapOfLists;
import forge.util.maps.MapOfLists;
public class HostedMatch {
private Match match;
private Game game;
private String title;
private final List<PlayerControllerHuman> humanControllers = Lists.newArrayList();
private Map<RegisteredPlayer, IGuiGame> guis;
private int humanCount;
private FControlGamePlayback playbackControl = null;
private final MatchUiEventVisitor visitor = new MatchUiEventVisitor();
private final Map<PlayerControllerHuman, NextGameDecision> nextGameDecisions = Maps.newHashMap();
public HostedMatch() {
}
private static GameRules getDefaultRules(final GameType gameType) {
final GameRules gameRules = new GameRules(gameType);
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.canCloneUseTargetsImage = FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE);
return gameRules;
}
public void startMatch(final GameType gameType, final Set<GameType> appliedVariants, final List<RegisteredPlayer> players, final RegisteredPlayer human, final IGuiGame gui) {
startMatch(getDefaultRules(gameType), appliedVariants, players, human, gui);
}
public void startMatch(final GameType gameType, final Set<GameType> appliedVariants, final List<RegisteredPlayer> players, final Map<RegisteredPlayer, IGuiGame> guis) {
startMatch(getDefaultRules(gameType), appliedVariants, players, guis);
}
public void startMatch(final GameRules gameRules, final Set<GameType> appliedVariants, final List<RegisteredPlayer> players, final RegisteredPlayer human, final IGuiGame gui) {
startMatch(gameRules, appliedVariants, players, human == null || gui == null ? null : ImmutableMap.of(human, gui));
}
public void startMatch(final GameRules gameRules, final Set<GameType> appliedVariants, final List<RegisteredPlayer> players, final Map<RegisteredPlayer, IGuiGame> guis) {
if (gameRules == null || gameRules.getGameType() == null || players == null || players.isEmpty()) {
throw new IllegalArgumentException();
}
this.guis = guis == null ? ImmutableMap.<RegisteredPlayer, IGuiGame>of() : guis;
final boolean useRandomFoil = FModel.getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL);
for (final RegisteredPlayer rp : players) {
rp.setRandomFoil(useRandomFoil);
}
if (appliedVariants != null && !appliedVariants.isEmpty()) {
gameRules.setAppliedVariants(appliedVariants);
}
if (players.size() == 2) {
title = String.format("%s vs %s", players.get(0).getPlayer().getName(), players.get(1).getPlayer().getName());
} else {
title = String.format("Multiplayer Game (%d players)", players.size());
}
this.match = new Match(gameRules, players, title);
startGame();
}
public void continueMatch() {
endCurrentGame();
startGame();
}
public void restartMatch() {
endCurrentGame();
this.match = new Match(match.getRules(), match.getPlayers(), this.title);
startGame();
}
public void startGame() {
nextGameDecisions.clear();
SoundSystem.instance.setBackgroundMusic(MusicPlaylist.MATCH);
game = match.createGame();
if (game.getRules().getGameType() == GameType.Quest) {
QuestController qc = FModel.getQuest();
// Reset new list when the Match round starts, not when each game starts
if (game.getMatch().getPlayedGames().isEmpty()) {
qc.getCards().resetNewList();
}
game.subscribeToEvents(qc); // this one listens to player's mulligans ATM
}
game.subscribeToEvents(SoundSystem.instance);
game.subscribeToEvents(visitor);
final String[] indices = FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",");
// Instantiate all required field slots (user at 0)
final List<Player> sortedPlayers = Lists.newArrayList(game.getRegisteredPlayers());
Collections.sort(sortedPlayers, new Comparator<Player>() {
@Override
public int compare(final Player p1, final Player p2) {
final int v1 = p1.getController() instanceof PlayerControllerHuman ? 0 : 1;
final int v2 = p2.getController() instanceof PlayerControllerHuman ? 0 : 1;
return Integer.compare(v1, v2);
}
});
final GameView gameView = getGameView();
int i = 0;
int avatarIndex = 0;
humanCount = 0;
final MapOfLists<IGuiGame, PlayerView> playersPerGui = new HashMapOfLists<IGuiGame, PlayerView>(CollectionSuppliers.<PlayerView>arrayLists());
for (final Player p : sortedPlayers) {
if (i < indices.length) {
avatarIndex = Integer.parseInt(indices[i]);
i++;
}
p.getLobbyPlayer().setAvatarIndex(avatarIndex);
p.updateAvatar();
if (p.getController() instanceof PlayerControllerHuman) {
final PlayerControllerHuman humanController = (PlayerControllerHuman) p.getController();
final IGuiGame gui = guis.get(p.getRegisteredPlayer());
humanController.setGui(gui);
gui.setGameView(gameView);
gui.setGameController(p.getView(), humanController);
game.subscribeToEvents(new FControlGameEventHandler(humanController));
playersPerGui.add(gui, p.getView());
humanControllers.add(humanController);
humanCount++;
}
}
for (final Entry<IGuiGame, Collection<PlayerView>> e : playersPerGui.entrySet()) {
e.getKey().openView(new FCollection<PlayerView>(e.getValue()));
}
if (humanCount == 0) { //watch game but do not participate
final IGuiGame gui = GuiBase.getInterface().getNewGuiGame();
gui.setGameView(gameView);
final PlayerControllerHuman humanController = new WatchLocalGame(game, new LobbyPlayerHuman("Spectator"), gui);
game.subscribeToEvents(new FControlGameEventHandler(humanController));
humanControllers.add(humanController);
gui.setGameController(null, humanController);
gui.openView(null);
} else if (humanCount == sortedPlayers.size()) {
//if there are no AI's, allow all players to see all cards (hotseat mode).
for (final PlayerControllerHuman humanController : humanControllers) {
humanController.setMayLookAtAllCards(true);
}
}
//prompt user for player one name if needed
if (StringUtils.isBlank(FModel.getPreferences().getPref(FPref.PLAYER_NAME)) && humanCount == 1) {
GamePlayerUtil.setPlayerName();
}
//ensure opponents set properly
for (final Player p : sortedPlayers) {
p.updateOpponentsForView();
}
// It's important to run match in a different thread to allow GUI inputs to be invoked from inside game.
// Game is set on pause while gui player takes decisions
game.getAction().invoke(new Runnable() {
@Override public void run() {
if (humanCount == 0) {
// Create FControlGamePlayback in game thread to allow pausing
playbackControl = new FControlGamePlayback(humanControllers.get(0));
playbackControl.setGame(game);
game.subscribeToEvents(playbackControl);
}
// Actually start the game!
match.startGame(game);
// After game is over...
if (humanCount == 0) {
// ... if no human players, let AI decide next game
addNextGameDecision(null, match.isMatchOver() ? NextGameDecision.QUIT : NextGameDecision.CONTINUE);
}
}
});
}
public void registerSpectator(final LobbyPlayer lobbyPlayer, final IGuiGame gui) {
final PlayerControllerHuman humanController = new WatchLocalGame(game, null, gui);
gui.setGameController(null, humanController);
gui.openView(null);
game.subscribeToEvents(new FControlGameEventHandler(humanController));
humanControllers.add(humanController);
}
public Game getGame() {
return game;
}
public GameView getGameView() {
return game == null ? null : game.getView();
}
public void endCurrentGame() {
if (game == null) { return; }
game = null;
for (final PlayerControllerHuman humanController : humanControllers) {
humanController.getGui().afterGameEnd();
}
humanControllers.clear();
}
public void pause() {
final ForgePreferences prefs = FModel.getPreferences();
if (prefs == null) { return; } //do nothing if prefs haven't been initialized yet
SoundSystem.instance.pause();
//pause playback if needed
if (prefs.getPrefBoolean(FPref.UI_PAUSE_WHILE_MINIMIZED) && playbackControl != null) {
playbackControl.getInput().pause();
}
}
public void resume() {
SoundSystem.instance.resume();
}
/** Returns a random name from the supplied list. */
public static String getRandomName() {
final String playerName = GuiDisplayUtil.getPlayerName();
final String aiName = NameGenerator.getRandomName("Any", "Generic", playerName);
return aiName;
}
private final class MatchUiEventVisitor implements IUiEventVisitor<Void> {
@Override
public Void visit(final UiEventBlockerAssigned event) {
for (final PlayerControllerHuman humanController : humanControllers) {
humanController.getGui().updateSingleCard(event.blocker);
final PlayerView p = humanController.getPlayer().getView();
if (event.attackerBeingBlocked.getController().equals(p)) {
humanController.getGui().autoPassCancel(p);
}
}
return null;
}
@Override
public Void visit(final UiEventAttackerDeclared event) {
for (final PlayerControllerHuman humanController : humanControllers) {
humanController.getGui().updateSingleCard(event.attacker);
}
return null;
}
@Override
public Void visit(final UiEventNextGameDecision event) {
addNextGameDecision(event.getController(), event.getDecision());
return null;
}
@Subscribe
public void receiveEvent(final UiEvent evt) {
evt.visit(this);
}
}
private void addNextGameDecision(final PlayerControllerHuman controller, final NextGameDecision decision) {
if (decision == NextGameDecision.QUIT) {
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public void run() {
endCurrentGame();
}
});
return; // if any player chooses quit, quit the match
}
nextGameDecisions.put(controller, decision);
if (nextGameDecisions.size() < humanControllers.size()) {
return;
}
int newMatch = 0, continueMatch = 0;
for (final NextGameDecision dec : nextGameDecisions.values()) {
switch (dec) {
case CONTINUE:
continueMatch++;
break;
case NEW:
newMatch++;
break;
default:
}
}
if (continueMatch >= newMatch) {
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public void run() {
continueMatch();
}
});
} else {
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override public void run() {
restartMatch();
}
});
}
}
}

View File

@@ -1,54 +0,0 @@
package forge.match;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import forge.LobbyPlayer;
import forge.game.GameEntityView;
import forge.game.Match;
import forge.game.card.CardView;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import forge.interfaces.IButton;
import forge.util.ITriggerEvent;
public interface IMatchController {
void startNewMatch(Match match);
boolean resetForNewGame();
boolean hotSeatMode();
void openView(List<Player> sortedPlayers);
void afterGameEnd();
void showCombat();
void showPromptMessage(PlayerView playerView, String message);
boolean stopAtPhase(PlayerView playerTurn, PhaseType phase);
IButton getBtnOK(PlayerView playerView);
IButton getBtnCancel(PlayerView playerView);
void focusButton(IButton button);
void flashIncorrectAction();
void updatePhase();
void updateTurn(PlayerView player);
void updatePlayerControl();
void enableOverlay();
void disableOverlay();
void finishGame();
Object showManaPool(PlayerView player);
void hideManaPool(PlayerView player, Object zoneToRestore);
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);
void restoreOldZones(Map<PlayerView, Object> playersToRestoreZonesFor);
void updateStack();
void updateZones(List<Pair<PlayerView, ZoneType>> zonesToUpdate);
void updateSingleCard(CardView card);
void refreshCardDetails(Iterable<CardView> cards);
void updateManaPool(Iterable<PlayerView> manaPoolUpdate);
void updateLives(Iterable<PlayerView> livesUpdate);
void setPanelSelection(CardView hostCard);
void hear(LobbyPlayer player, String message);
SpellAbility getAbilityToPlay(List<SpellAbility> abilities, ITriggerEvent triggerEvent);
Map<CardView, Integer> assignDamage(CardView attacker, List<CardView> blockers, int damage, GameEntityView defender, boolean overrideOrder);
}

View File

@@ -1,459 +0,0 @@
package forge.match;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import forge.LobbyPlayer;
import forge.ai.LobbyPlayerAi;
import forge.card.CardStateName;
import forge.control.FControlGameEventHandler;
import forge.control.FControlGamePlayback;
import forge.control.WatchLocalGame;
import forge.events.IUiEventVisitor;
import forge.events.UiEvent;
import forge.events.UiEventAttackerDeclared;
import forge.events.UiEventBlockerAssigned;
import forge.game.Game;
import forge.game.GameEntityView;
import forge.game.GameRules;
import forge.game.GameType;
import forge.game.GameView;
import forge.game.Match;
import forge.game.card.CardView;
import forge.game.card.CardView.CardStateView;
import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.player.RegisteredPlayer;
import forge.match.input.InputPlaybackControl;
import forge.match.input.InputQueue;
import forge.model.FModel;
import forge.player.GamePlayerUtil;
import forge.player.LobbyPlayerHuman;
import forge.player.PlayerControllerHuman;
import forge.properties.ForgePreferences;
import forge.properties.ForgePreferences.FPref;
import forge.quest.QuestController;
import forge.sound.MusicPlaylist;
import forge.sound.SoundSystem;
import forge.util.GuiDisplayUtil;
import forge.util.NameGenerator;
import forge.util.gui.SOptionPane;
public class MatchUtil {
private static IMatchController controller;
private static Game game;
private static Player currentPlayer;
private static final List<PlayerControllerHuman> humanControllers = new ArrayList<PlayerControllerHuman>();
private static int humanCount;
private static final EventBus uiEvents;
private static FControlGamePlayback playbackControl;
private static final MatchUiEventVisitor visitor = new MatchUiEventVisitor();
static {
uiEvents = new EventBus("ui events");
uiEvents.register(SoundSystem.instance);
uiEvents.register(visitor);
}
public static IMatchController getController() {
return controller;
}
public static void setController(IMatchController controller0) {
controller = controller0;
}
public static void startMatch(GameType gameType, List<RegisteredPlayer> players) {
startMatch(gameType, null, players);
}
public static void startMatch(GameType gameType, Set<GameType> appliedVariants, List<RegisteredPlayer> players) {
boolean useRandomFoil = FModel.getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL);
for (RegisteredPlayer rp : players) {
rp.setRandomFoil(useRandomFoil);
}
GameRules rules = new GameRules(gameType);
if (appliedVariants != null && !appliedVariants.isEmpty()) {
rules.setAppliedVariants(appliedVariants);
}
rules.setPlayForAnte(FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE));
rules.setMatchAnteRarity(FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE_MATCH_RARITY));
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
rules.canCloneUseTargetsImage = FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE);
controller.startNewMatch(new Match(rules, players));
}
public static void continueMatch() {
final Match match = game.getMatch();
endCurrentGame();
startGame(match);
}
public static void restartMatch() {
final Match match = game.getMatch();
endCurrentGame();
match.clearGamesPlayed();
startGame(match);
}
public static void startGame(final Match match) {
if (!controller.resetForNewGame()) { return; }
SoundSystem.instance.setBackgroundMusic(MusicPlaylist.MATCH);
game = match.createGame();
if (game.getRules().getGameType() == GameType.Quest) {
QuestController qc = FModel.getQuest();
// Reset new list when the Match round starts, not when each game starts
if (game.getMatch().getPlayedGames().isEmpty()) {
qc.getCards().resetNewList();
}
game.subscribeToEvents(qc); // this one listens to player's mulligans ATM
}
game.subscribeToEvents(SoundSystem.instance);
final String[] indices = FModel.getPreferences().getPref(FPref.UI_AVATARS).split(",");
// Instantiate all required field slots (user at 0)
final List<Player> sortedPlayers = new ArrayList<Player>(game.getRegisteredPlayers());
Collections.sort(sortedPlayers, new Comparator<Player>() {
@Override
public int compare(Player p1, Player p2) {
int v1 = p1.getController() instanceof PlayerControllerHuman ? 0 : 1;
int v2 = p2.getController() instanceof PlayerControllerHuman ? 0 : 1;
return Integer.compare(v1, v2);
}
});
int i = 0;
int avatarIndex = 0;
humanCount = 0;
for (Player p : sortedPlayers) {
if (i < indices.length) {
avatarIndex = Integer.parseInt(indices[i]);
i++;
}
p.getLobbyPlayer().setAvatarIndex(avatarIndex);
p.updateAvatar();
if (p.getController() instanceof PlayerControllerHuman) {
final PlayerControllerHuman humanController = (PlayerControllerHuman) p.getController();
if (humanCount == 0) {
currentPlayer = p;
game.subscribeToEvents(new FControlGameEventHandler(humanController));
}
humanControllers.add(humanController);
humanCount++;
}
}
if (humanCount == 0) { //watch game but do not participate
currentPlayer = sortedPlayers.get(0);
PlayerControllerHuman humanController = new WatchLocalGame(game, currentPlayer, currentPlayer.getLobbyPlayer());
game.subscribeToEvents(new FControlGameEventHandler(humanController));
humanControllers.add(humanController);
}
else if (humanCount == sortedPlayers.size() && controller.hotSeatMode()) {
//if there are no AI's, allow all players to see all cards (hotseat mode).
for (Player p : sortedPlayers) {
((PlayerControllerHuman) p.getController()).setMayLookAtAllCards(true);
}
}
controller.openView(sortedPlayers);
if (humanCount == 0) {
playbackControl = new FControlGamePlayback(getHumanController());
playbackControl.setGame(game);
game.subscribeToEvents(playbackControl);
}
//ensure opponents set properly
for (Player p : sortedPlayers) {
p.updateOpponentsForView();
}
// It's important to run match in a different thread to allow GUI inputs to be invoked from inside game.
// Game is set on pause while gui player takes decisions
game.getAction().invoke(new Runnable() {
@Override
public void run() {
//prompt user for player one name if needed
if (StringUtils.isBlank(FModel.getPreferences().getPref(FPref.PLAYER_NAME))) {
boolean isPlayerOneHuman = match.getPlayers().get(0).getPlayer() instanceof LobbyPlayerHuman;
boolean isPlayerTwoComputer = match.getPlayers().get(1).getPlayer() instanceof LobbyPlayerAi;
if (isPlayerOneHuman && isPlayerTwoComputer) {
GamePlayerUtil.setPlayerName();
}
}
match.startGame(game);
}
});
}
public static Game getGame() {
return game;
}
public static GameView getGameView() {
return game == null ? null : game.getView();
}
public static PlayerControllerHuman getHumanController() {
return getHumanController(currentPlayer);
}
public static PlayerControllerHuman getHumanController(Player player) {
switch (humanControllers.size()) {
case 1:
return humanControllers.get(0);
case 0:
return null;
default:
return humanControllers.get(player.getId());
}
}
public static int getHumanCount() {
return humanCount;
}
public static PlayerControllerHuman getOtherHumanController() {
//return other game view besides current game view
if (humanControllers.size() < 2) {
return null;
}
PlayerControllerHuman humanController = getHumanController();
if (humanController == humanControllers.get(0)) {
return humanControllers.get(1);
}
return humanControllers.get(0);
}
public static InputQueue getInputQueue() {
PlayerControllerHuman humanController = getHumanController();
if (humanController != null) {
return humanController.getInputQueue();
}
return null;
}
public static void endCurrentTurn() {
getHumanController().passPriorityUntilEndOfTurn();
}
public static Player getCurrentPlayer() {
return currentPlayer;
}
public static void setCurrentPlayer(Player currentPlayer0) {
if (currentPlayer == currentPlayer0) { return; }
currentPlayer = currentPlayer0;
if (humanControllers.size() > 1) {
//TODO: ensure card views updated when current player changes to account for changes in card visibility
}
}
public static void alphaStrike() {
getHumanController().alphaStrike();
}
public static Map<CardView, Integer> getDamageToAssign(final CardView attacker, final List<CardView> blockers, final int damage, final GameEntityView defender, final boolean overrideOrder) {
if (damage <= 0) {
return new HashMap<CardView, Integer>();
}
// If the first blocker can absorb all of the damage, don't show the Assign Damage dialog
CardView firstBlocker = blockers.get(0);
if (!overrideOrder && !attacker.getCurrentState().hasDeathtouch() && firstBlocker.getLethalDamage() >= damage) {
Map<CardView, Integer> res = new HashMap<CardView, Integer>();
res.put(firstBlocker, damage);
return res;
}
return controller.assignDamage(attacker, blockers, damage, defender, overrideOrder);
}
public static String getCardImageKey(CardStateView csv) {
if (currentPlayer == null) { return csv.getImageKey(null); } //if not in game, card can be shown
return csv.getImageKey(currentPlayer.getView());
}
public static boolean canCardBeShown(CardView cv) {
if (currentPlayer == null) { return true; } //if not in game, card can be shown
if (currentPlayer.getController() instanceof PlayerControllerHuman && ((PlayerControllerHuman) currentPlayer.getController()).mayLookAtAllCards()) {
return true;
}
return cv.canBeShownTo(currentPlayer.getView());
}
public static boolean canCardBeFlipped(CardView cv) {
if (cv == null) { return false; }
CardStateView altState = cv.getAlternateState();
if (altState == null) { return false; }
switch (altState.getState()) {
case Original:
CardStateView currentState = cv.getCurrentState();
if (currentState.getState() == CardStateName.FaceDown) {
return currentPlayer == null || cv.canFaceDownBeShownTo(currentPlayer.getView());
}
return true; //original can always be shown if not a face down that can't be shown
case Flipped:
case Transformed:
return true;
default:
return false;
}
}
private static Set<PlayerView> highlightedPlayers = new HashSet<PlayerView>();
public static void setHighlighted(PlayerView pv, boolean b) {
if (b) {
highlightedPlayers.add(pv);
}
else {
highlightedPlayers.remove(pv);
}
}
public static boolean isHighlighted(PlayerView player) {
return highlightedPlayers.contains(player);
}
private static Set<CardView> highlightedCards = new HashSet<CardView>();
// used to highlight cards in UI
public static void setUsedToPay(CardView card, boolean value) {
boolean hasChanged = value ? highlightedCards.add(card) : highlightedCards.remove(card);
if (hasChanged) { // since we are in UI thread, may redraw the card right now
controller.updateSingleCard(card);
}
}
public static boolean isUsedToPay(CardView card) {
return highlightedCards.contains(card);
}
public static void updateCards(Iterable<CardView> cardsToUpdate) {
for (CardView c : cardsToUpdate) {
controller.updateSingleCard(c);
}
}
/** Concede game, bring up WinLose UI. */
public static void concede() {
String userPrompt =
"This will end the current game and you will not be able to resume.\n\n" +
"Concede anyway?";
if (SOptionPane.showConfirmDialog(userPrompt, "Concede Game?", "Concede", "Cancel")) {
if (humanCount == 0) { // no human? then all players surrender!
for (Player p : game.getPlayers()) {
p.concede();
}
}
else {
getCurrentPlayer().concede();
}
Player priorityPlayer = game.getPhaseHandler().getPriorityPlayer();
boolean humanHasPriority = priorityPlayer == null || priorityPlayer.getLobbyPlayer() instanceof LobbyPlayerHuman;
if (humanCount > 0 && humanHasPriority) {
game.getAction().checkGameOverCondition();
}
else {
game.isGameOver(); // this is synchronized method - it's used to make Game-0 thread see changes made here
onGameOver(false); //release any waiting input, effectively passing priority
}
if (playbackControl != null) {
playbackControl.onGameStopRequested();
}
}
}
public static void onGameOver(boolean releaseAllInputs) {
for (PlayerControllerHuman humanController : humanControllers) {
humanController.getInputQueue().onGameOver(releaseAllInputs);
}
}
public static void endCurrentGame() {
if (game == null) { return; }
game = null;
currentPlayer = null;
humanControllers.clear();
controller.afterGameEnd();
}
public static void pause() {
ForgePreferences prefs = FModel.getPreferences();
if (prefs == null) { return; } //do nothing if prefs haven't been initialized yet
SoundSystem.instance.pause();
//pause playback if needed
if (prefs.getPrefBoolean(FPref.UI_PAUSE_WHILE_MINIMIZED)) {
InputQueue inputQueue = getInputQueue();
if (inputQueue != null && inputQueue.getInput() instanceof InputPlaybackControl) {
((InputPlaybackControl) inputQueue.getInput()).pause();
}
}
}
public static void resume() {
SoundSystem.instance.resume();
}
private final static boolean LOG_UIEVENTS = false;
// UI-related events should arrive here
public static void fireEvent(UiEvent uiEvent) {
if (LOG_UIEVENTS) {
//System.out.println("UI: " + uiEvent.toString() + " \t\t " + FThreads.debugGetStackTraceItem(4, true));
}
uiEvents.post(uiEvent);
}
/** Returns a random name from the supplied list. */
public static String getRandomName() {
String playerName = GuiDisplayUtil.getPlayerName();
String aiName = NameGenerator.getRandomName("Any", "Generic", playerName);
return aiName;
}
public final static LobbyPlayer getGuiPlayer() {
return GamePlayerUtil.getGuiPlayer();
}
private static class MatchUiEventVisitor implements IUiEventVisitor<Void> {
@Override
public Void visit(UiEventBlockerAssigned event) {
controller.updateSingleCard(event.blocker);
return null;
}
@Override
public Void visit(UiEventAttackerDeclared event) {
controller.updateSingleCard(event.attacker);
return null;
}
@Subscribe
public void receiveEvent(UiEvent evt) {
evt.visit(this);
}
}
}

View File

@@ -0,0 +1,7 @@
package forge.match;
public enum NextGameDecision {
NEW,
CONTINUE,
QUIT;
}

View File

@@ -1,46 +0,0 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.match.input;
import forge.game.player.PlayerView;
import forge.interfaces.IButton;
import forge.match.MatchUtil;
/**
* Manages match UI OK/Cancel button enabling and focus
*/
public class ButtonUtil {
public static void update(PlayerView owner, boolean okEnabled, boolean cancelEnabled, boolean focusOk) {
update(owner, "OK", "Cancel", okEnabled, cancelEnabled, focusOk);
}
public static void update(PlayerView owner, String okLabel, String cancelLabel, boolean okEnabled, boolean cancelEnabled, boolean focusOk) {
IButton btnOk = MatchUtil.getController().getBtnOK(owner);
IButton btnCancel = MatchUtil.getController().getBtnCancel(owner);
btnOk.setText(okLabel);
btnCancel.setText(cancelLabel);
btnOk.setEnabled(okEnabled);
btnCancel.setEnabled(cancelEnabled);
if (okEnabled && focusOk) {
MatchUtil.getController().focusButton(btnOk);
}
else if (cancelEnabled) {
MatchUtil.getController().focusButton(btnCancel);
}
}
}

View File

@@ -39,7 +39,6 @@ import forge.game.combat.CombatUtil;
import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.zone.ZoneType;
import forge.match.MatchUtil;
import forge.player.PlayerControllerHuman;
import forge.util.FCollectionView;
import forge.util.ITriggerEvent;
@@ -89,10 +88,9 @@ public class InputAttack extends InputSyncronizedBase {
private void updatePrompt() {
if (canCallBackAttackers()) {
ButtonUtil.update(getOwner(), "OK", "Call Back", true, true, true);
}
else {
ButtonUtil.update(getOwner(), "OK", "Alpha Strike", true, true, true);
getController().getGui().updateButtons(getOwner(), "OK", "Call Back", true, true, true);
} else {
getController().getGui().updateButtons(getOwner(), "OK", "Alpha Strike", true, true, true);
}
}
@@ -137,7 +135,7 @@ public class InputAttack extends InputSyncronizedBase {
}
}
}
MatchUtil.updateCards(refreshCards);
getController().getGui().updateCards(refreshCards);
updateMessage();
}
@@ -147,7 +145,7 @@ public class InputAttack extends InputSyncronizedBase {
setCurrentDefender(selected);
}
else {
MatchUtil.getController().flashIncorrectAction(); // cannot attack that player
getController().getGui().flashIncorrectAction(); // cannot attack that player
}
}
@@ -260,18 +258,18 @@ public class InputAttack extends InputSyncronizedBase {
combat.addAttacker(card, currentDefender, activeBand);
activateBand(activeBand);
MatchUtil.fireEvent(new UiEventAttackerDeclared(
card.getGame().fireEvent(new UiEventAttackerDeclared(
CardView.get(card),
GameEntityView.get(currentDefender)));
}
private boolean undeclareAttacker(final Card card) {
combat.removeFromCombat(card);
MatchUtil.setUsedToPay(CardView.get(card), false);
getController().getGui().setUsedToPay(CardView.get(card), false);
// When removing an attacker clear the attacking band
activateBand(null);
MatchUtil.fireEvent(new UiEventAttackerDeclared(
card.getGame().fireEvent(new UiEventAttackerDeclared(
CardView.get(card), null));
return true;
}
@@ -280,10 +278,10 @@ public class InputAttack extends InputSyncronizedBase {
currentDefender = def;
for (final GameEntity ge : defenders) {
if (ge instanceof Card) {
MatchUtil.setUsedToPay(CardView.get((Card) ge), ge == def);
getController().getGui().setUsedToPay(CardView.get((Card) ge), ge == def);
}
else if (ge instanceof Player) {
MatchUtil.setHighlighted(PlayerView.get((Player) ge), ge == def);
getController().getGui().setHighlighted(PlayerView.get((Player) ge), ge == def);
}
}
@@ -293,14 +291,14 @@ public class InputAttack extends InputSyncronizedBase {
private final void activateBand(final AttackingBand band) {
if (activeBand != null) {
for (final Card card : activeBand.getAttackers()) {
MatchUtil.setUsedToPay(CardView.get(card), false);
getController().getGui().setUsedToPay(CardView.get(card), false);
}
}
activeBand = band;
if (activeBand != null) {
for (final Card card : activeBand.getAttackers()) {
MatchUtil.setUsedToPay(CardView.get(card), true);
getController().getGui().setUsedToPay(CardView.get(card), true);
}
}
}
@@ -324,6 +322,6 @@ public class InputAttack extends InputSyncronizedBase {
showMessage(message);
updatePrompt();
MatchUtil.getController().showCombat(); // redraw sword icons
getController().getGui().showCombat(); // redraw sword icons
}
}

View File

@@ -18,17 +18,13 @@
package forge.match.input;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import forge.FThreads;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.phase.PhaseHandler;
import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.match.MatchUtil;
import forge.player.PlayerControllerHuman;
import forge.util.ITriggerEvent;
@@ -41,8 +37,8 @@ import forge.util.ITriggerEvent;
* @version $Id: InputBase.java 24769 2014-02-09 13:56:04Z Hellfish $
*/
public abstract class InputBase implements java.io.Serializable, Input {
/** Constant <code>serialVersionUID=-6539552513871194081L</code>. */
private static final long serialVersionUID = -6539552513871194081L;
/** Constant <code>serialVersionUID=-2531867688249685076L</code>. */
private static final long serialVersionUID = -2531867688249685076L;
private final PlayerControllerHuman controller;
public InputBase(final PlayerControllerHuman controller0) {
@@ -52,7 +48,8 @@ public abstract class InputBase implements java.io.Serializable, Input {
return controller;
}
public PlayerView getOwner() {
return controller.getPlayer().getView();
final Player owner = getController().getPlayer();
return owner == null ? null : owner.getView();
}
private boolean finished = false;
@@ -61,7 +58,7 @@ public abstract class InputBase implements java.io.Serializable, Input {
finished = true;
if (allowAwaitNextInput()) {
awaitNextInput(controller);
controller.awaitNextInput();
}
}
@@ -69,66 +66,11 @@ public abstract class InputBase implements java.io.Serializable, Input {
return false;
}
private static final Timer awaitNextInputTimer = new Timer();
private static TimerTask awaitNextInputTask;
public static void awaitNextInput(final PlayerControllerHuman controller) {
//delay updating prompt to await next input briefly so buttons don't flicker disabled then enabled
awaitNextInputTask = new TimerTask() {
@Override
public void run() {
FThreads.invokeInEdtLater(new Runnable() {
@Override
public void run() {
synchronized (awaitNextInputTimer) {
if (awaitNextInputTask != null) {
updatePromptForAwait(controller);
awaitNextInputTask = null;
}
}
}
});
}
};
awaitNextInputTimer.schedule(awaitNextInputTask, 250);
}
public static void waitForOtherPlayer() {
final PlayerControllerHuman controller = MatchUtil.getOtherHumanController();
if (controller == null) { return; }
cancelAwaitNextInput();
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
updatePromptForAwait(controller);
}
});
}
private static void updatePromptForAwait(final PlayerControllerHuman controller) {
PlayerView playerView = controller.getLocalPlayerView();
MatchUtil.getController().showPromptMessage(playerView, "Waiting for opponent...");
ButtonUtil.update(playerView, false, false, false);
}
public static void cancelAwaitNextInput() {
synchronized (awaitNextInputTimer) { //ensure task doesn't reset awaitNextInputTask during this block
if (awaitNextInputTask != null) {
try {
awaitNextInputTask.cancel(); //cancel timer once next input shown if needed
}
catch (Exception ex) {} //suppress any exception thrown by cancel()
awaitNextInputTask = null;
}
}
}
// showMessage() is always the first method called
@Override
public final void showMessageInitial() {
finished = false;
cancelAwaitNextInput();
controller.cancelAwaitNextInput();
showMessage();
}
@@ -172,7 +114,7 @@ public abstract class InputBase implements java.io.Serializable, Input {
// to remove need for CMatchUI dependence
protected final void showMessage(final String message) {
MatchUtil.getController().showPromptMessage(getOwner(), message);
controller.getGui().showPromptMessage(getOwner(), message);
}
protected String getTurnPhasePriorityMessage(final Game game) {

View File

@@ -30,11 +30,9 @@ import forge.game.combat.CombatUtil;
import forge.game.event.GameEventCombatChanged;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.match.MatchUtil;
import forge.player.PlayerControllerHuman;
import forge.util.ITriggerEvent;
import forge.util.ThreadUtil;
import forge.util.gui.SGuiDialog;
/**
* <p>
@@ -78,7 +76,7 @@ public class InputBlock extends InputSyncronizedBase {
@Override
protected final void showMessage() {
// could add "Reset Blockers" button
ButtonUtil.update(getOwner(), true, false, true);
getController().getGui().updateButtons(getOwner(), true, false, true);
if (currentAttacker == null) {
showMessage("Select another attacker to declare blockers for.");
@@ -89,7 +87,7 @@ public class InputBlock extends InputSyncronizedBase {
showMessage(message);
}
MatchUtil.getController().showCombat();
getController().getGui().showCombat();
}
/** {@inheritDoc} */
@@ -106,7 +104,7 @@ public class InputBlock extends InputSyncronizedBase {
ThreadUtil.invokeInGameThread(new Runnable() {
@Override
public void run() {
SGuiDialog.message(blockErrors);
getController().getGui().message(blockErrors);
}
});
}
@@ -118,7 +116,7 @@ public class InputBlock extends InputSyncronizedBase {
boolean isCorrectAction = false;
if (triggerEvent != null && triggerEvent.getButton() == 3 && card.getController() == defender) {
combat.removeFromCombat(card);
MatchUtil.fireEvent(new UiEventBlockerAssigned(
card.getGame().fireEvent(new UiEventBlockerAssigned(
CardView.get(card), (CardView) null));
isCorrectAction = true;
}
@@ -134,7 +132,7 @@ public class InputBlock extends InputSyncronizedBase {
if (combat.isBlocking(card, currentAttacker)) {
//if creature already blocking current attacker, remove blocker from combat
combat.removeBlockAssignment(currentAttacker, card);
MatchUtil.fireEvent(new UiEventBlockerAssigned(
card.getGame().fireEvent(new UiEventBlockerAssigned(
CardView.get(card), (CardView) null));
isCorrectAction = true;
}
@@ -142,7 +140,7 @@ public class InputBlock extends InputSyncronizedBase {
isCorrectAction = CombatUtil.canBlock(currentAttacker, card, combat);
if (isCorrectAction) {
combat.addBlocker(currentAttacker, card);
MatchUtil.fireEvent(new UiEventBlockerAssigned(
card.getGame().fireEvent(new UiEventBlockerAssigned(
CardView.get(card),
CardView.get(currentAttacker)));
}
@@ -178,7 +176,7 @@ public class InputBlock extends InputSyncronizedBase {
private void setCurrentAttacker(final Card card) {
currentAttacker = card;
for (final Card c : combat.getAttackers()) {
MatchUtil.setUsedToPay(CardView.get(c), card == c);
getController().getGui().setUsedToPay(CardView.get(c), card == c);
}
}
}

View File

@@ -57,7 +57,7 @@ public class InputConfirm extends InputSyncronizedBase {
/** {@inheritDoc} */
@Override
protected final void showMessage() {
ButtonUtil.update(getOwner(), yesButtonText, noButtonText, true, true, defaultYes);
getController().getGui().updateButtons(getOwner(), yesButtonText, noButtonText, true, true, defaultYes);
showMessage(message);
}

View File

@@ -26,12 +26,10 @@ import forge.game.card.CardCollectionView;
import forge.game.card.CardView;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.match.MatchUtil;
import forge.player.PlayerControllerHuman;
import forge.util.ITriggerEvent;
import forge.util.Lang;
import forge.util.ThreadUtil;
import forge.util.gui.SGuiDialog;
/**
* <p>
@@ -74,11 +72,11 @@ public class InputConfirmMulligan extends InputSyncronizedBase {
}
if (isCommander) {
ButtonUtil.update(getOwner(), "Keep", "Exile", true, false, true);
getController().getGui().updateButtons(getOwner(), "Keep", "Exile", true, false, true);
sb.append("Will you keep your hand or choose some cards to exile those and draw one less card?");
}
else {
ButtonUtil.update(getOwner(), "Keep", "Mulligan", true, true, true);
getController().getGui().updateButtons(getOwner(), "Keep", "Mulligan", true, true, true);
sb.append("Do you want to keep your hand?");
}
@@ -103,7 +101,7 @@ public class InputConfirmMulligan extends InputSyncronizedBase {
if (isCommander) {
// Clear the "selected" icon after clicking the done button
for (final Card c : this.selected) {
MatchUtil.setUsedToPay(c.getView(), false);
getController().getGui().setUsedToPay(c.getView(), false);
}
}
stop();
@@ -121,7 +119,7 @@ public class InputConfirmMulligan extends InputSyncronizedBase {
}
final CardView cView = c0.getView();
if (isSerumPowder && SGuiDialog.confirm(cView, "Use " + cView + "'s ability?")) {
if (isSerumPowder && getController().getGui().confirm(cView, "Use " + cView + "'s ability?")) {
cardSelectLocked = true;
ThreadUtil.invokeInGameThread(new Runnable() {
public void run() {
@@ -138,14 +136,14 @@ public class InputConfirmMulligan extends InputSyncronizedBase {
if (isCommander) { // allow to choose cards for partial paris
if (selected.contains(c0)) {
MatchUtil.setUsedToPay(c0.getView(), false);
getController().getGui().setUsedToPay(c0.getView(), false);
selected.remove(c0);
}
else {
MatchUtil.setUsedToPay(c0.getView(), true);
getController().getGui().setUsedToPay(c0.getView(), true);
selected.add(c0);
}
ButtonUtil.update(getOwner(), "Keep", "Exile", true, !selected.isEmpty(), true);
getController().getGui().updateButtons(getOwner(), "Keep", "Exile", true, !selected.isEmpty(), true);
}
return true;
}

View File

@@ -9,7 +9,7 @@ import forge.game.card.Card;
import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
import forge.match.MatchUtil;
import forge.player.PlayerControllerHuman;
import forge.util.ITriggerEvent;
import forge.util.ThreadUtil;
@@ -18,9 +18,11 @@ public class InputLockUI implements Input {
private final InputQueue inputQueue;
private final Game game;
public InputLockUI(final Game game0, final InputQueue inputQueue0) {
private final PlayerControllerHuman controller;
public InputLockUI(final Game game0, final InputQueue inputQueue0, final PlayerControllerHuman controller) {
game = game0;
inputQueue = inputQueue0;
this.controller = controller;
}
@Override
@@ -32,7 +34,7 @@ public class InputLockUI implements Input {
int ixCall = 1 + iCall.getAndIncrement();
ThreadUtil.delay(500, new InputUpdater(ixCall));
}
@Override
public String toString() {
return "lockUI";
@@ -56,7 +58,7 @@ public class InputLockUI implements Input {
private final Runnable showMessageFromEdt = new Runnable() {
@Override
public void run() {
ButtonUtil.update(InputLockUI.this.getOwner(), "", "", false, false, false);
controller.getGui().updateButtons(InputLockUI.this.getOwner(), "", "", false, false, false);
showMessage("Waiting for actions...");
}
};
@@ -66,7 +68,7 @@ public class InputLockUI implements Input {
}
protected void showMessage(String message) {
MatchUtil.getController().showPromptMessage(getOwner(), message);
controller.getGui().showPromptMessage(getOwner(), message);
}
@Override

View File

@@ -31,7 +31,6 @@ import forge.player.PlayerControllerHuman;
import forge.properties.ForgePreferences.FPref;
import forge.util.ITriggerEvent;
import forge.util.ThreadUtil;
import forge.util.gui.SOptionPane;
/**
* <p>
@@ -44,25 +43,23 @@ import forge.util.gui.SOptionPane;
public class InputPassPriority extends InputSyncronizedBase {
/** Constant <code>serialVersionUID=-581477682214137181L</code>. */
private static final long serialVersionUID = -581477682214137181L;
private final Player player;
private List<SpellAbility> chosenSa;
public InputPassPriority(final PlayerControllerHuman controller, final Player human) {
public InputPassPriority(final PlayerControllerHuman controller) {
super(controller);
player = human;
}
/** {@inheritDoc} */
@Override
public final void showMessage() {
showMessage(getTurnPhasePriorityMessage(player.getGame()));
showMessage(getTurnPhasePriorityMessage(getController().getGame()));
chosenSa = null;
if (getController().canUndoLastAction()) { //allow undoing with cancel button if can undo last action
ButtonUtil.update(getOwner(), "OK", "Undo", true, true, true);
getController().getGui().updateButtons(getOwner(), "OK", "Undo", true, true, true);
}
else { //otherwise allow ending turn with cancel button
ButtonUtil.update(getOwner(), "OK", "End Turn", true, true, true);
getController().getGui().updateButtons(getOwner(), "OK", "End Turn", true, true, true);
}
}
@@ -85,7 +82,7 @@ public class InputPassPriority extends InputSyncronizedBase {
passPriority(new Runnable() {
@Override
public void run() {
player.getController().autoPassUntilEndOfTurn();
getController().autoPassUntilEndOfTurn();
stop();
}
});
@@ -94,13 +91,13 @@ public class InputPassPriority extends InputSyncronizedBase {
@Override
protected boolean allowAwaitNextInput() {
return chosenSa == null && !player.getController().mayAutoPass(); //don't allow awaiting next input if player chose to end the turn or if a spell/ability is chosen
return chosenSa == null && !getController().mayAutoPass(); //don't allow awaiting next input if player chose to end the turn or if a spell/ability is chosen
}
private void passPriority(final Runnable runnable) {
if (FModel.getPreferences().getPrefBoolean(FPref.UI_MANA_LOST_PROMPT)) {
//if gui player has mana floating that will be lost if phase ended right now, prompt before passing priority
final Game game = player.getGame();
final Game game = getController().getGame();
if (game.getStack().isEmpty()) { //phase can't end right now if stack isn't empty
Player player = game.getPhaseHandler().getPriorityPlayer();
if (player != null && player.getManaPool().willManaBeLostAtEndOfPhase() && player.getLobbyPlayer() == GamePlayerUtil.getGuiPlayer()) {
@@ -111,7 +108,7 @@ public class InputPassPriority extends InputSyncronizedBase {
if (FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN)) {
message += " You will take mana burn damage equal to the amount of floating mana lost this way.";
}
if (SOptionPane.showOptionDialog(message, "Mana Floating", SOptionPane.WARNING_ICON, new String[]{"OK", "Cancel"}) == 0) {
if (getController().getGui().showConfirmDialog(message, "Mana Floating", "Ok", "Cancel")) {
runnable.run();
}
}
@@ -128,12 +125,12 @@ public class InputPassPriority extends InputSyncronizedBase {
@Override
protected boolean onCardSelected(final Card card, final List<Card> otherCardsToSelect, final ITriggerEvent triggerEvent) {
//remove unplayable unless triggerEvent specified, in which case unplayable may be shown as disabled options
List<SpellAbility> abilities = card.getAllPossibleAbilities(player, triggerEvent == null);
List<SpellAbility> abilities = card.getAllPossibleAbilities(getController().getPlayer(), triggerEvent == null);
if (abilities.isEmpty()) {
return false;
}
final SpellAbility ability = player.getController().getAbilityToPlay(abilities, triggerEvent);
final SpellAbility ability = getController().getAbilityToPlay(abilities, triggerEvent);
if (ability != null) {
chosenSa = new ArrayList<SpellAbility>();
chosenSa.add(ability);
@@ -141,7 +138,7 @@ public class InputPassPriority extends InputSyncronizedBase {
//if mana ability activated, activate same ability on other cards to select if possible
String abStr = ability.toUnsuppressedString();
for (Card c : otherCardsToSelect) {
for (SpellAbility ab : c.getAllPossibleAbilities(player, true)) {
for (SpellAbility ab : c.getAllPossibleAbilities(getController().getPlayer(), true)) {
if (ab.toUnsuppressedString().equals(abStr)) {
chosenSa.add(ab);
break;
@@ -155,12 +152,12 @@ public class InputPassPriority extends InputSyncronizedBase {
}
@Override
public String getActivateAction(Card card) {
List<SpellAbility> abilities = card.getAllPossibleAbilities(player, true);
public String getActivateAction(final Card card) {
final List<SpellAbility> abilities = card.getAllPossibleAbilities(getController().getPlayer(), true);
if (abilities.isEmpty()) {
return null;
}
SpellAbility sa = abilities.get(0);
final SpellAbility sa = abilities.get(0);
if (sa.isSpell()) {
return "cast spell";
}

View File

@@ -24,16 +24,13 @@ import forge.game.player.PlayerView;
import forge.game.replacement.ReplacementEffect;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.SpellAbility;
import forge.match.MatchUtil;
import forge.player.HumanPlay;
import forge.player.PlayerControllerHuman;
import forge.util.Evaluator;
import forge.util.ITriggerEvent;
import forge.util.gui.SGuiChoose;
public abstract class InputPayMana extends InputSyncronizedBase {
private static final long serialVersionUID = -9133423708688480255L;
private static final long serialVersionUID = 718128600948280315L;
protected int phyLifeToLose = 0;
@@ -58,13 +55,13 @@ public abstract class InputPayMana extends InputSyncronizedBase {
//if player is floating mana, show mana pool to make it easier to use that mana
wasFloatingMana = !player.getManaPool().isEmpty();
zoneToRestore = wasFloatingMana ? MatchUtil.getController().showManaPool(PlayerView.get(player)) : null;
zoneToRestore = wasFloatingMana ? getController().getGui().showManaPool(PlayerView.get(player)) : null;
}
@Override
protected void onStop() {
if (wasFloatingMana) { //hide mana pool if it was shown due to floating mana
MatchUtil.getController().hideManaPool(PlayerView.get(player), zoneToRestore);
getController().getGui().hideManaPool(PlayerView.get(player), zoneToRestore);
}
}
@@ -273,9 +270,8 @@ public abstract class InputPayMana extends InputSyncronizedBase {
final SpellAbility chosen;
if (chosenAbility == null) {
chosen = abilities.size() > 1 && choice ? SGuiChoose.one("Choose mana ability", abilities) : abilities.get(0);
}
else {
chosen = abilities.size() > 1 && choice ? getController().getGui().one("Choose mana ability", abilities) : abilities.get(0);
} else {
chosen = chosenAbility;
}
ColorSet colors = ColorSet.fromMask(0 == colorNeeded ? colorCanUse : colorNeeded);
@@ -387,10 +383,9 @@ public abstract class InputPayMana extends InputSyncronizedBase {
protected void updateButtons() {
if (supportAutoPay()) {
ButtonUtil.update(getOwner(), "Auto", "Cancel", false, true, false);
}
else {
ButtonUtil.update(getOwner(), "", "Cancel", false, true, false);
getController().getGui().updateButtons(getOwner(), "Auto", "Cancel", false, true, false);
} else {
getController().getGui().updateButtons(getOwner(), "", "Cancel", false, true, false);
}
}
@@ -412,7 +407,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
canPayManaCost = proc.getResult();
}
if (canPayManaCost) { //enabled Auto button if mana cost can be paid
ButtonUtil.update(getOwner(), "Auto", "Cancel", true, true, true);
getController().getGui().updateButtons(getOwner(), "Auto", "Cancel", true, true, true);
}
}
showMessage(getMessage());

View File

@@ -4,10 +4,8 @@ import forge.control.FControlGamePlayback;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.phase.PhaseHandler;
import forge.game.player.PlayerView;
import forge.match.MatchUtil;
public class InputPlaybackControl extends InputSyncronizedBase implements InputSynchronized {
public class InputPlaybackControl extends InputSyncronizedBase {
private static final long serialVersionUID = 7979208993306642072L;
final FControlGamePlayback control;
@@ -17,17 +15,12 @@ public class InputPlaybackControl extends InputSyncronizedBase implements InputS
private final Game game;
public InputPlaybackControl(final Game game0, final FControlGamePlayback fControlGamePlayback) {
super(null);
super(fControlGamePlayback.getController());
game = game0;
control = fControlGamePlayback;
setPause(false);
}
@Override
public PlayerView getOwner() {
return MatchUtil.getHumanController().getLocalPlayerView();
}
@Override
protected void showMessage() {
setPause(false);
@@ -39,8 +32,7 @@ public class InputPlaybackControl extends InputSyncronizedBase implements InputS
if (isPaused) {
showMessage(getTurnPhasePriorityMessage(game));
currentTurn = 0;
}
else {
} else {
final PhaseHandler ph = game.getPhaseHandler();
if (currentTurn == ph.getTurn()) { return; }
@@ -49,13 +41,12 @@ public class InputPlaybackControl extends InputSyncronizedBase implements InputS
}
}
private void setPause(boolean pause) {
private void setPause(final boolean pause) {
isPaused = pause;
if (isPaused) {
ButtonUtil.update(getOwner(), "Resume", "Step", true, true, true);
}
else {
ButtonUtil.update(getOwner(), "Pause", isFast ? "1x Speed" : "10x Faster", true, true, true);
getController().getGui().updateButtons(null, "Resume", "Step", true, true, true);
} else {
getController().getGui().updateButtons(null, "Pause", isFast ? "1x Speed" : "10x Faster", true, true, true);
}
}

View File

@@ -13,7 +13,6 @@ import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.player.PlayerControllerHuman;
import forge.util.ITriggerEvent;
import forge.util.gui.SGuiChoose;
public final class InputProliferate extends InputSelectManyBase<GameEntity> {
private static final long serialVersionUID = -1779224307654698954L;
@@ -61,7 +60,7 @@ public final class InputProliferate extends InputSelectManyBase<GameEntity> {
}
}
CounterType toAdd = choices.size() == 1 ? choices.get(0) : SGuiChoose.one("Select counter type", choices);
CounterType toAdd = choices.size() == 1 ? choices.get(0) : getController().getGui().one("Select counter type", choices);
chosenCounters.put(card, toAdd);
}

View File

@@ -61,7 +61,10 @@ public class InputProxy implements Observer {
game.getPhaseHandler().debugPrintState(), Singletons.getControl().getInputQueue().printInputStack());
*/
input.set(nextInput);
Runnable showMessage = new Runnable() {
if (!(nextInput instanceof InputLockUI)) {
controller.getGui().setCurrentPlayer(nextInput.getOwner());
}
final Runnable showMessage = new Runnable() {
@Override
public void run() {
Input current = getInput();
@@ -70,7 +73,6 @@ public class InputProxy implements Observer {
current.showMessageInitial();
}
};
FThreads.invokeInEdtLater(showMessage);
}
/**
@@ -79,7 +81,7 @@ public class InputProxy implements Observer {
* </p>
*/
public final void selectButtonOK() {
Input inp = getInput();
final Input inp = getInput();
if (inp != null) {
inp.selectButtonOK();
}

View File

@@ -22,7 +22,6 @@ import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import forge.game.Game;
import forge.match.MatchUtil;
import forge.player.PlayerControllerHuman;
/**
@@ -35,11 +34,9 @@ import forge.player.PlayerControllerHuman;
*/
public class InputQueue extends Observable {
private final BlockingDeque<InputSynchronized> inputStack = new LinkedBlockingDeque<InputSynchronized>();
private final InputLockUI inputLock;
private final Game game;
public InputQueue(final Game game, final InputProxy inputProxy) {
inputLock = new InputLockUI(game, this);
this.game = game;
addObserver(inputProxy);
}
@@ -63,11 +60,11 @@ public class InputQueue extends Observable {
}
public final Input getActualInput(final PlayerControllerHuman controller) {
Input topMost = inputStack.peek(); // incoming input to Control
if (topMost != null && !controller.getGame().isGameOver()) {
final Input topMost = inputStack.peek(); // incoming input to Control
if (topMost != null && !game.isGameOver()) {
return topMost;
}
return inputLock;
return new InputLockUI(game, this, controller);
} // getInput()
// only for debug purposes
@@ -76,23 +73,22 @@ public class InputQueue extends Observable {
}
public void setInput(final InputSynchronized input) {
if (MatchUtil.getHumanCount() > 1) { //update current player if needed
MatchUtil.setCurrentPlayer(game.getPlayer(input.getOwner()));
}
//if (HostedMatch.getHumanCount() > 1) { //update current player if needed
//HostedMatch.setCurrentPlayer(game.getPlayer(input.getOwner()));
//}
inputStack.push(input);
InputBase.waitForOtherPlayer();
syncPoint();
updateObservers();
}
public void syncPoint() {
synchronized (inputLock) {
void syncPoint() {
synchronized (this) {
// acquire and release lock, so that actions from Game thread happen before EDT reads their results
}
}
public void onGameOver(boolean releaseAllInputs) {
for (InputSynchronized inp : inputStack) {
public void onGameOver(final boolean releaseAllInputs) {
for (final InputSynchronized inp : inputStack) {
inp.relaseLatchWhenGameIsOver();
if (!releaseAllInputs) {
break;

View File

@@ -7,7 +7,6 @@ import com.google.common.collect.Iterables;
import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.card.CardView;
import forge.match.MatchUtil;
import forge.player.PlayerControllerHuman;
public abstract class InputSelectManyBase<T extends GameEntity> extends InputSyncronizedBase {
@@ -46,7 +45,7 @@ public abstract class InputSelectManyBase<T extends GameEntity> extends InputSyn
@Override
public final void showMessage() {
showMessage(getMessage());
ButtonUtil.update(getOwner(), hasEnoughTargets(), allowCancel, true);
getController().getGui().updateButtons(getOwner(), hasEnoughTargets(), allowCancel, true);
}
@Override
@@ -76,14 +75,14 @@ public abstract class InputSelectManyBase<T extends GameEntity> extends InputSyn
protected void onSelectStateChanged(final GameEntity c, final boolean newState) {
if (c instanceof Card) {
MatchUtil.setUsedToPay(CardView.get((Card) c), newState); // UI supports card highlighting though this abstraction-breaking mechanism
getController().getGui().setUsedToPay(CardView.get((Card) c), newState); // UI supports card highlighting though this abstraction-breaking mechanism
}
}
private void resetUsedToPay() {
for (final GameEntity c : getSelected()) {
if (c instanceof Card) {
MatchUtil.setUsedToPay(CardView.get((Card) c), false);
getController().getGui().setUsedToPay(CardView.get((Card) c), false);
}
}
}

View File

@@ -14,11 +14,8 @@ import forge.game.card.CardView;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.match.MatchUtil;
import forge.player.PlayerControllerHuman;
import forge.util.ITriggerEvent;
import forge.util.gui.SGuiChoose;
public final class InputSelectTargets extends InputSyncronizedBase {
private final List<Card> choices;
@@ -70,19 +67,19 @@ public final class InputSelectTargets extends InputSyncronizedBase {
if (!tgt.isMinTargetsChosen(sa.getHostCard(), sa) || tgt.isDividedAsYouChoose()) {
if (mandatory && tgt.hasCandidates(sa, true)) {
// Player has to click on a target
ButtonUtil.update(getOwner(), false, false, false);
getController().getGui().updateButtons(getOwner(), false, false, false);
}
else {
ButtonUtil.update(getOwner(), false, true, false);
getController().getGui().updateButtons(getOwner(), false, true, false);
}
}
else {
if (mandatory && tgt.hasCandidates(sa, true)) {
// Player has to click on a target or ok
ButtonUtil.update(getOwner(), true, false, true);
getController().getGui().updateButtons(getOwner(), true, false, true);
}
else {
ButtonUtil.update(getOwner(), true, true, true);
getController().getGui().updateButtons(getOwner(), true, true, true);
}
}
}
@@ -170,7 +167,7 @@ public final class InputSelectTargets extends InputSyncronizedBase {
final StringBuilder sb = new StringBuilder();
sb.append(apiBasedMessage);
sb.append(card.toString());
Integer chosen = SGuiChoose.oneOrNone(sb.toString(), choices);
final Integer chosen = getController().getGui().oneOrNone(sb.toString(), choices);
if (chosen == null) {
return true; //still return true since there was a valid choice
}
@@ -226,7 +223,7 @@ public final class InputSelectTargets extends InputSyncronizedBase {
final StringBuilder sb = new StringBuilder();
sb.append(apiBasedMessage);
sb.append(player.getName());
Integer chosen = SGuiChoose.oneOrNone(sb.toString(), choices);
final Integer chosen = getController().getGui().oneOrNone(sb.toString(), choices);
if (null == chosen) {
return;
}
@@ -243,7 +240,7 @@ public final class InputSelectTargets extends InputSyncronizedBase {
private void addTarget(final GameEntity ge) {
sa.getTargets().add(ge);
if (ge instanceof Card) {
MatchUtil.setUsedToPay(CardView.get((Card) ge), true);
getController().getGui().setUsedToPay(CardView.get((Card) ge), true);
lastTarget = (Card) ge;
}
final Integer val = targetDepth.get(ge);
@@ -260,7 +257,7 @@ public final class InputSelectTargets extends InputSyncronizedBase {
private void done() {
for (final GameEntity c : targetDepth.keySet()) {
if (c instanceof Card) {
MatchUtil.setUsedToPay(CardView.get((Card) c), false);
getController().getGui().setUsedToPay(CardView.get((Card) c), false);
}
}

View File

@@ -27,6 +27,7 @@ import java.util.Set;
import com.google.common.base.Predicate;
import forge.FThreads;
import forge.GuiBase;
import forge.ImageKeys;
import forge.LobbyPlayer;
import forge.card.CardRarity;
@@ -37,12 +38,11 @@ import forge.deck.Deck;
import forge.game.GameRules;
import forge.game.GameType;
import forge.game.GameView;
import forge.game.Match;
import forge.game.player.RegisteredPlayer;
import forge.interfaces.IButton;
import forge.interfaces.IWinLoseView;
import forge.item.PaperCard;
import forge.match.MatchUtil;
import forge.match.HostedMatch;
import forge.model.FModel;
import forge.planarconquest.ConquestPlaneData.RegionData;
import forge.planarconquest.ConquestPreferences.CQPref;
@@ -253,8 +253,8 @@ public class ConquestController {
variants.add(GameType.Planechase);
}
RegisteredPlayer humanStart = new RegisteredPlayer(commander.getDeck());
RegisteredPlayer aiStart = new RegisteredPlayer(opponent.getDeck());
final RegisteredPlayer humanStart = new RegisteredPlayer(commander.getDeck());
final RegisteredPlayer aiStart = new RegisteredPlayer(opponent.getDeck());
if (isHumanDefending) { //give human a small life bonus if defending
humanStart.setStartingLife(humanStart.getStartingLife() + prefs.getPrefInt(CQPref.DEFEND_BONUS_LIFE));
@@ -276,28 +276,28 @@ public class ConquestController {
aiPlayerName += " (AI)"; //ensure player names are distinct
}
List<RegisteredPlayer> starter = new ArrayList<RegisteredPlayer>();
final List<RegisteredPlayer> starter = new ArrayList<RegisteredPlayer>();
humanPlayer = new LobbyPlayerHuman(humanPlayerName);
humanPlayer.setAvatarCardImageKey(ImageKeys.getImageKey(commander.getCard(), false));
starter.add(humanStart.setPlayer(humanPlayer));
LobbyPlayer aiPlayer = GamePlayerUtil.createAiPlayer(aiPlayerName, -1);
final LobbyPlayer aiPlayer = GamePlayerUtil.createAiPlayer(aiPlayerName, -1);
aiPlayer.setAvatarCardImageKey(ImageKeys.getImageKey(opponent.getCard(), false));
starter.add(aiStart.setPlayer(aiPlayer));
boolean useRandomFoil = FModel.getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL);
for(RegisteredPlayer rp : starter) {
final boolean useRandomFoil = FModel.getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL);
for (final RegisteredPlayer rp : starter) {
rp.setRandomFoil(useRandomFoil);
}
GameRules rules = new GameRules(GameType.PlanarConquest);
final GameRules rules = new GameRules(GameType.PlanarConquest);
rules.setGamesPerMatch(1); //only play one game at a time
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
rules.canCloneUseTargetsImage = FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE);
final Match mc = new Match(rules, starter);
final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch();
FThreads.invokeInEdtNowOrLater(new Runnable(){
@Override
public void run() {
MatchUtil.startGame(mc);
hostedMatch.startMatch(rules, null, starter, humanStart, GuiBase.getInterface().getNewGuiGame());
}
});
}

View File

@@ -7,7 +7,6 @@ import forge.LobbyPlayer;
import forge.ai.AiProfileUtil;
import forge.ai.LobbyPlayerAi;
import forge.game.player.Player;
import forge.match.MatchUtil;
import forge.model.FModel;
import forge.properties.ForgePreferences.FPref;
import forge.util.GuiDisplayUtil;
@@ -87,17 +86,6 @@ public final class GamePlayerUtil {
newPlayerName = getVerifiedPlayerName(getPlayerNameUsingStandardPrompt(oldPlayerName), oldPlayerName);
}
//update name for player in active game if needed
if (MatchUtil.getGame() != null) {
for (Player player : MatchUtil.getGame().getPlayers()) {
if (player.getLobbyPlayer() == MatchUtil.getGuiPlayer()) {
player.setName(newPlayerName);
player.getLobbyPlayer().setName(newPlayerName);
break;
}
}
}
FModel.getPreferences().setPref(FPref.PLAYER_NAME, newPlayerName);
FModel.getPreferences().save();

View File

@@ -1,5 +1,13 @@
package forge.player;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -16,7 +24,34 @@ import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets;
import forge.game.card.CardView;
import forge.game.card.CounterType;
import forge.game.cost.*;
import forge.game.cost.CostAddMana;
import forge.game.cost.CostChooseCreatureType;
import forge.game.cost.CostDamage;
import forge.game.cost.CostDecisionMakerBase;
import forge.game.cost.CostDiscard;
import forge.game.cost.CostDraw;
import forge.game.cost.CostExile;
import forge.game.cost.CostExileFromStack;
import forge.game.cost.CostExiledMoveToGrave;
import forge.game.cost.CostFlipCoin;
import forge.game.cost.CostGainControl;
import forge.game.cost.CostGainLife;
import forge.game.cost.CostMill;
import forge.game.cost.CostPartMana;
import forge.game.cost.CostPayLife;
import forge.game.cost.CostPutCardToLib;
import forge.game.cost.CostPutCounter;
import forge.game.cost.CostRemoveAnyCounter;
import forge.game.cost.CostRemoveCounter;
import forge.game.cost.CostReturn;
import forge.game.cost.CostReveal;
import forge.game.cost.CostSacrifice;
import forge.game.cost.CostTap;
import forge.game.cost.CostTapType;
import forge.game.cost.CostUnattach;
import forge.game.cost.CostUntap;
import forge.game.cost.CostUntapType;
import forge.game.cost.PaymentDecision;
import forge.game.player.Player;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbility;
@@ -28,11 +63,6 @@ import forge.util.Aggregates;
import forge.util.FCollectionView;
import forge.util.ITriggerEvent;
import forge.util.Lang;
import forge.util.gui.SGuiChoose;
import forge.util.gui.SGuiDialog;
import java.util.*;
import java.util.Map.Entry;
public class HumanCostDecision extends CostDecisionMakerBase {
private final PlayerControllerHuman controller;
@@ -298,18 +328,18 @@ public class HumanCostDecision extends CostDecisionMakerBase {
return PaymentDecision.number(0);
}
final Game game = controller.getGame();
final Player p = game.getPlayer(SGuiChoose.oneOrNone(String.format("Exile from whose %s?", cost.getFrom()), PlayerView.getCollection(payableZone)));
final Player p = game.getPlayer(controller.getGui().oneOrNone(String.format("Exile from whose %s?", cost.getFrom()), PlayerView.getCollection(payableZone)));
if (p == null) {
return null;
}
CardCollection typeList = CardLists.filter(list, CardPredicates.isOwner(p));
int count = typeList.size();
final CardCollection typeList = CardLists.filter(list, CardPredicates.isOwner(p));
final int count = typeList.size();
if (count < nNeeded) {
return null;
}
CardCollection toExile = game.getCardList(SGuiChoose.many("Exile from " + cost.getFrom(), "To be exiled", nNeeded, CardView.getCollection(typeList), null));
final CardCollection toExile = game.getCardList(controller.getGui().many("Exile from " + cost.getFrom(), "To be exiled", nNeeded, CardView.getCollection(typeList), null));
return PaymentDecision.card(toExile);
}
@@ -357,7 +387,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
List<SpellAbility> exiled = new ArrayList<SpellAbility>();
for (int i = 0; i < c; i++) {
//Have to use the stack descriptions here because some copied spells have no description otherwise
final String o = SGuiChoose.oneOrNone("Exile from Stack", descList);
final String o = controller.getGui().oneOrNone("Exile from Stack", descList);
if (o != null) {
final SpellAbility toExile = saList.get(descList.indexOf(o));
@@ -393,7 +423,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
CardCollection exiled = new CardCollection();
for (int i = 0; i < nNeeded; i++) {
final Card c = getCard(SGuiChoose.oneOrNone("Exile from " + cost.getFrom(), CardView.getCollection(typeList)));
final Card c = getCard(controller.getGui().oneOrNone("Exile from " + cost.getFrom(), CardView.getCollection(typeList)));
if (c == null) { return null; }
typeList.remove(c);
@@ -422,7 +452,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
if (list.size() < c) {
return null;
}
final CardCollection choice = controller.getGame().getCardList(SGuiChoose.many("Choose an exiled card to put into graveyard", "To graveyard", c, CardView.getCollection(list), CardView.get(source)));
final CardCollection choice = controller.getGame().getCardList(controller.getGui().many("Choose an exiled card to put into graveyard", "To graveyard", c, CardView.getCollection(list), CardView.get(source)));
return PaymentDecision.card(choice);
}
@@ -494,7 +524,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
final StringBuilder sb = new StringBuilder();
sb.append(source.getName()).append(" - Choose an opponent to gain ").append(c).append(" life:");
final Player chosenToGain = controller.getGame().getPlayer(SGuiChoose.oneOrNone(sb.toString(), PlayerView.getCollection(oppsThatCanGainLife)));
final Player chosenToGain = controller.getGame().getPlayer(controller.getGui().oneOrNone(sb.toString(), PlayerView.getCollection(oppsThatCanGainLife)));
if (chosenToGain == null) {
return null;
}
@@ -604,7 +634,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
CardCollection chosen = new CardCollection();
for (int i = 0; i < nNeeded; i++) {
final Card c = getCard(SGuiChoose.oneOrNone("Put from " + fromZone + " to library", CardView.getCollection(typeList)));
final Card c = getCard(controller.getGui().oneOrNone("Put from " + fromZone + " to library", CardView.getCollection(typeList)));
if (c == null) {
return null;
}
@@ -619,7 +649,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
return PaymentDecision.number(0);
}
final Player p = controller.getGame().getPlayer(SGuiChoose.oneOrNone(String.format("Put cards from whose %s?", fromZone), PlayerView.getCollection(payableZone)));
final Player p = controller.getGame().getPlayer(controller.getGui().oneOrNone(String.format("Put cards from whose %s?", fromZone), PlayerView.getCollection(payableZone)));
if (p == null) {
return null;
}
@@ -631,7 +661,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
CardCollection chosen = new CardCollection();
for (int i = 0; i < nNeeded; i++) {
final Card c = getCard(SGuiChoose.oneOrNone("Put cards from " + fromZone + " to Library", CardView.getCollection(typeList)));
final Card c = getCard(controller.getGui().oneOrNone("Put cards from " + fromZone + " to Library", CardView.getCollection(typeList)));
if (c == null) {
return null;
}
@@ -805,7 +835,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
}
String prompt = "Select type counters to remove";
cost.setCounterType(SGuiChoose.one(prompt, typeChoices));
cost.setCounterType(controller.getGui().one(prompt, typeChoices));
return PaymentDecision.card(selected, cost.getCounter());
}
@@ -903,7 +933,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
int maxCounters = source.getCounters(cost.counter);
if (amount.equals("All")) {
final CardView view = CardView.get(ability.getHostCard());
if (!SGuiDialog.confirm(view, "Remove all counters?")) {
if (!controller.getGui().confirm(view, "Remove all counters?")) {
return null;
}
cntRemoved = maxCounters;
@@ -959,7 +989,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
}
}
final Card card = getCard(SGuiChoose.oneOrNone("Remove counter(s) from a card in " + cost.zone, suspended));
final Card card = getCard(controller.getGui().oneOrNone("Remove counter(s) from a card in " + cost.zone, suspended));
return null == card ? null : PaymentDecision.card(card, c);
}

View File

@@ -450,7 +450,7 @@ public class HumanPlay {
}
if (typeChoices.size() > 1) {
String cprompt = "Select type counters to remove";
counterType = SGuiChoose.one(cprompt, typeChoices);
counterType = controller.getGui().one(cprompt, typeChoices);
}
else {
counterType = typeChoices.get(0);

View File

@@ -5,7 +5,6 @@ import forge.game.Game;
import forge.game.player.IGameEntitiesFactory;
import forge.game.player.Player;
import forge.game.player.PlayerController;
import forge.match.MatchUtil;
import forge.util.GuiDisplayUtil;
public class LobbyPlayerHuman extends LobbyPlayer implements IGameEntitiesFactory {
@@ -19,14 +18,14 @@ public class LobbyPlayerHuman extends LobbyPlayer implements IGameEntitiesFactor
}
@Override
public Player createIngamePlayer(Game game, final int id) {
Player player = new Player(GuiDisplayUtil.personalizeHuman(getName()), game, id);
PlayerControllerHuman controller = new PlayerControllerHuman(game, player, this);
public Player createIngamePlayer(final Game game, final int id) {
final Player player = new Player(GuiDisplayUtil.personalizeHuman(getName()), game, id);
final PlayerControllerHuman controller = new PlayerControllerHuman(game, player, this);
player.setFirstController(controller);
return player;
}
public void hear(LobbyPlayer player, String message) {
MatchUtil.getController().hear(player, message);
//ostedMatch.getController().hear(player, message);
}
}

View File

@@ -41,6 +41,7 @@ import forge.control.FControlGamePlayback;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckSection;
import forge.events.UiEventNextGameDecision;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameEntityView;
@@ -63,7 +64,6 @@ import forge.game.cost.Cost;
import forge.game.cost.CostPart;
import forge.game.cost.CostPartMana;
import forge.game.mana.Mana;
import forge.game.phase.PhaseType;
import forge.game.player.DelayedReveal;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
@@ -80,13 +80,14 @@ import forge.game.trigger.WrappedAbility;
import forge.game.zone.MagicStack;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.interfaces.IDevModeCheats;
import forge.interfaces.IGameController;
import forge.interfaces.IGuiGame;
import forge.item.IPaperCard;
import forge.item.PaperCard;
import forge.match.MatchUtil;
import forge.match.input.ButtonUtil;
import forge.match.NextGameDecision;
import forge.match.input.Input;
import forge.match.input.InputAttack;
import forge.match.input.InputBase;
import forge.match.input.InputBlock;
import forge.match.input.InputConfirm;
import forge.match.input.InputConfirmMulligan;
@@ -107,8 +108,6 @@ import forge.util.ITriggerEvent;
import forge.util.Lang;
import forge.util.MessageUtil;
import forge.util.TextUtil;
import forge.util.gui.SGuiChoose;
import forge.util.gui.SGuiDialog;
import forge.util.gui.SOptionPane;
/**
@@ -116,7 +115,9 @@ import forge.util.gui.SOptionPane;
*
* Handles phase skips for now.
*/
public class PlayerControllerHuman extends PlayerController {
public class PlayerControllerHuman
extends PlayerController
implements IGameController {
/**
* Cards this player may look at right now, for example when searching a
* library.
@@ -124,20 +125,29 @@ public class PlayerControllerHuman extends PlayerController {
private boolean mayLookAtAllCards = false;
private boolean disableAutoYields = false;
private IGuiGame gui;
protected final InputQueue inputQueue;
protected final InputProxy inputProxy;
public PlayerControllerHuman(Game game0, Player p, LobbyPlayer lp) {
public PlayerControllerHuman(final Game game0, final Player p, final LobbyPlayer lp) {
super(game0, p, lp);
inputProxy = new InputProxy(this);
inputQueue = new InputQueue(game, inputProxy);
}
public PlayerControllerHuman(Player p, LobbyPlayer lp, PlayerControllerHuman owner) {
public PlayerControllerHuman(final Player p, final LobbyPlayer lp, final PlayerControllerHuman owner) {
super(owner.getGame(), p, lp);
gui = owner.gui;
inputProxy = owner.inputProxy;
inputQueue = owner.getInputQueue();
}
public final IGuiGame getGui() {
return gui;
}
public final void setGui(final IGuiGame gui) {
this.gui = gui;
}
public final InputQueue getInputQueue() {
return inputQueue;
}
@@ -147,7 +157,7 @@ public class PlayerControllerHuman extends PlayerController {
}
public PlayerView getLocalPlayerView() {
return player.getView();
return player == null ? null : player.getView();
}
public boolean getDisableAutoYields() {
@@ -157,12 +167,13 @@ public class PlayerControllerHuman extends PlayerController {
disableAutoYields = disableAutoYields0;
}
@Override
public boolean mayLookAtAllCards() {
return mayLookAtAllCards;
}
private final HashSet<Card> tempShownCards = new HashSet<Card>();
public <T> void tempShow(Iterable<T> objects) {
public <T> void tempShow(final Iterable<T> objects) {
for (final T t : objects) {
if (t instanceof Card) {
// assume you may see any card passed through here
@@ -170,22 +181,22 @@ public class PlayerControllerHuman extends PlayerController {
}
}
}
private void tempShowCard(Card c) {
private void tempShowCard(final Card c) {
if (c == null) { return; }
tempShownCards.add(c);
c.setMayLookAt(player, true, true);
}
private void tempShowCards(Iterable<Card> cards) {
private void tempShowCards(final Iterable<Card> cards) {
if (mayLookAtAllCards) { return; } //no needed if this is set
for (Card c : cards) {
for (final Card c : cards) {
tempShowCard(c);
}
}
private void endTempShowCards() {
if (tempShownCards.isEmpty()) { return; }
for (Card c : tempShownCards) {
for (final Card c : tempShownCards) {
c.setMayLookAt(player, false, true);
}
tempShownCards.clear();
@@ -202,15 +213,12 @@ public class PlayerControllerHuman extends PlayerController {
this.mayLookAtAllCards = mayLookAtAllCards;
}
public boolean isUiSetToSkipPhase(final Player turn, final PhaseType phase) {
return !MatchUtil.getController().stopAtPhase(PlayerView.get(turn), phase);
}
/**
* Uses GUI to learn which spell the player (human in our case) would like to play
*/
public SpellAbility getAbilityToPlay(final List<SpellAbility> abilities, final ITriggerEvent triggerEvent) {
return MatchUtil.getController().getAbilityToPlay(abilities, triggerEvent);
return getGui().getAbilityToPlay(abilities, triggerEvent);
//return HostedMatch.getController().getAbilityToPlay(abilities, triggerEvent);
}
@Override
@@ -258,11 +266,11 @@ public class PlayerControllerHuman extends PlayerController {
else {
errMsg = String.format("Too many cards in your sideboard (maximum %d), please make modifications to your deck again.", sbMax);
}
SOptionPane.showErrorDialog(errMsg, "Invalid Deck");
getGui().showErrorDialog(errMsg, "Invalid Deck");
}
// 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
newMain = GuiBase.getInterface().sideboard(sideboard, main);
newMain = getGui().sideboard(sideboard, main);
} while (conform && (newMain.size() < deckMinSize || combinedDeckSize - newMain.size() > sbMax));
return newMain;
@@ -283,7 +291,7 @@ public class PlayerControllerHuman extends PlayerController {
if ((attacker.hasKeyword("Trample") && defender != null) || (blockers.size() > 1)) {
final CardView vAttacker = CardView.get(attacker);
final GameEntityView vDefender = GameEntityView.get(defender);
final Map<CardView, Integer> result = MatchUtil.getDamageToAssign(vAttacker, vBlockers, damageDealt, vDefender, overrideOrder);
final Map<CardView, Integer> result = getGui().assignDamage(vAttacker, vBlockers, damageDealt, vDefender, overrideOrder);
for (final Entry<CardView, Integer> e : result.entrySet()) {
map.put(game.getCard(e.getKey()), e.getValue());
}
@@ -298,13 +306,13 @@ public class PlayerControllerHuman extends PlayerController {
private final boolean assignDamageAsIfNotBlocked(final Card attacker) {
return attacker.hasKeyword("CARDNAME assigns its combat damage as though it weren't blocked.")
|| (attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
&& SGuiDialog.confirm(CardView.get(attacker), "Do you want to assign its combat damage as though it weren't blocked?"));
&& getGui().confirm(CardView.get(attacker), "Do you want to assign its combat damage as though it weren't blocked?"));
}
@Override
public Integer announceRequirements(SpellAbility ability, String announce, boolean canChooseZero) {
int min = canChooseZero ? 0 : 1;
return SGuiChoose.getInteger("Choose " + announce + " for " + ability.getHostCard().getName(),
return getGui().getInteger("Choose " + announce + " for " + ability.getHostCard().getName(),
min, Integer.MAX_VALUE, min + 9);
}
@@ -347,7 +355,7 @@ public class PlayerControllerHuman extends PlayerController {
return singleChosen == null ? CardCollection.EMPTY : new CardCollection(singleChosen);
}
MatchUtil.getController().setPanelSelection(CardView.get(sa.getHostCard()));
getGui().setPanelSelection(CardView.get(sa.getHostCard()));
// try to use InputSelectCardsFromList when possible
boolean cardsAreInMyHandOrBattlefield = true;
@@ -369,7 +377,7 @@ public class PlayerControllerHuman extends PlayerController {
}
tempShowCards(sourceList);
final CardCollection choices = getGame().getCardList(SGuiChoose.many(title, "Chosen Cards", min, max, CardView.getCollection(sourceList), CardView.get(sa.getHostCard())));
final CardCollection choices = getGame().getCardList(getGui().many(title, "Chosen Cards", min, max, CardView.getCollection(sourceList), CardView.get(sa.getHostCard())));
endTempShowCards();
return choices;
@@ -417,7 +425,7 @@ public class PlayerControllerHuman extends PlayerController {
return Iterables.getFirst(input.getSelected(), null);
}
final GameEntityView result = GuiBase.getInterface().chooseSingleEntityForEffect(title, optionList, delayedReveal, isOptional, this);
final GameEntityView result = getGui().chooseSingleEntityForEffect(title, optionList, delayedReveal, isOptional, this);
endTempShowCards(); //assume tempShow called by GuiBase.getInterface().chooseSingleEntityForEffect
if (result instanceof CardView) {
return (T) game.getCard((CardView)result);
@@ -437,12 +445,12 @@ public class PlayerControllerHuman extends PlayerController {
for (int i = 0; i <= max - min; i++) {
choices[i] = Integer.valueOf(i + min);
}
return SGuiChoose.one(title, choices).intValue();
return getGui().one(title, choices).intValue();
}
@Override
public int chooseNumber(SpellAbility sa, String title, List<Integer> choices, Player relatedPlayer) {
return SGuiChoose.one(title, choices).intValue();
return getGui().one(title, choices).intValue();
}
@Override
@@ -452,7 +460,7 @@ public class PlayerControllerHuman extends PlayerController {
}
// Human is supposed to read the message and understand from it what to choose
return SGuiChoose.one(title, spells);
return getGui().one(title, spells);
}
/* (non-Javadoc)
@@ -460,26 +468,26 @@ public class PlayerControllerHuman extends PlayerController {
*/
@Override
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) {
return SGuiDialog.confirm(CardView.get(sa.getHostCard()), message);
return getGui().confirm(CardView.get(sa.getHostCard()), message);
}
@Override
public boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode bidlife,
String string, int bid, Player winner) {
return SGuiDialog.confirm(CardView.get(sa.getHostCard()), string + " Highest Bidder " + winner);
return getGui().confirm(CardView.get(sa.getHostCard()), string + " Highest Bidder " + winner);
}
@Override
public boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message) {
return SGuiDialog.confirm(CardView.get(hostCard), message);
return getGui().confirm(CardView.get(hostCard), message);
}
@Override
public boolean confirmTrigger(SpellAbility sa, Trigger regtrig, Map<String, String> triggerParams, boolean isMandatory) {
if (this.shouldAlwaysAcceptTrigger(regtrig.getId())) {
if (getGui().shouldAlwaysAcceptTrigger(regtrig.getId())) {
return true;
}
if (this.shouldAlwaysDeclineTrigger(regtrig.getId())) {
if (getGui().shouldAlwaysDeclineTrigger(regtrig.getId())) {
return false;
}
@@ -535,22 +543,22 @@ public class PlayerControllerHuman extends PlayerController {
@Override
public CardCollection orderBlockers(final Card attacker, final CardCollection blockers) {
final CardView vAttacker = CardView.get(attacker);
MatchUtil.getController().setPanelSelection(vAttacker);
return game.getCardList(SGuiChoose.order("Choose Damage Order for " + vAttacker, "Damaged First", CardView.getCollection(blockers), vAttacker));
getGui().setPanelSelection(vAttacker);
return game.getCardList(getGui().order("Choose Damage Order for " + vAttacker, "Damaged First", CardView.getCollection(blockers), vAttacker));
}
@Override
public CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) {
final CardView vAttacker = CardView.get(attacker);
MatchUtil.getController().setPanelSelection(vAttacker);
return game.getCardList(SGuiChoose.insertInList("Choose blocker after which to place " + vAttacker + " in damage order; cancel to place it first", CardView.get(blocker), CardView.getCollection(oldBlockers)));
getGui().setPanelSelection(vAttacker);
return game.getCardList(getGui().insertInList("Choose blocker after which to place " + vAttacker + " in damage order; cancel to place it first", CardView.get(blocker), CardView.getCollection(oldBlockers)));
}
@Override
public CardCollection orderAttackers(final Card blocker, final CardCollection attackers) {
final CardView vBlocker = CardView.get(blocker);
MatchUtil.getController().setPanelSelection(vBlocker);
return game.getCardList(SGuiChoose.order("Choose Damage Order for " + vBlocker, "Damaged First", CardView.getCollection(attackers), vBlocker));
getGui().setPanelSelection(vBlocker);
return game.getCardList(getGui().order("Choose Damage Order for " + vBlocker, "Damaged First", CardView.getCollection(attackers), vBlocker));
}
@Override
@@ -564,11 +572,11 @@ public class PlayerControllerHuman extends PlayerController {
String fm = MessageUtil.formatMessage(message, player, owner);
if (!cards.isEmpty()) {
tempShowCards(cards);
SGuiChoose.reveal(fm, CardView.getCollection(cards));
getGui().reveal(fm, CardView.getCollection(cards));
endTempShowCards();
}
else {
SGuiDialog.message(MessageUtil.formatMessage("There are no cards in {player's} " +
getGui().message(MessageUtil.formatMessage("There are no cards in {player's} " +
zone.name().toLowerCase(), player, owner), fm);
}
}
@@ -588,7 +596,7 @@ public class PlayerControllerHuman extends PlayerController {
}
}
else {
toBottom = game.getCardList(SGuiChoose.many("Select cards to be put on the bottom of your library", "Cards to put on the bottom", -1, CardView.getCollection(topN), null));
toBottom = game.getCardList(getGui().many("Select cards to be put on the bottom of your library", "Cards to put on the bottom", -1, CardView.getCollection(topN), null));
topN.removeAll((Collection<?>)toBottom);
if (topN.isEmpty()) {
toTop = null;
@@ -597,7 +605,7 @@ public class PlayerControllerHuman extends PlayerController {
toTop = topN;
}
else {
toTop = game.getCardList(SGuiChoose.order("Arrange cards to be put on top of your library", "Top of Library", CardView.getCollection(topN), null));
toTop = game.getCardList(getGui().order("Arrange cards to be put on top of your library", "Top of Library", CardView.getCollection(topN), null));
}
}
endTempShowCards();
@@ -609,7 +617,7 @@ public class PlayerControllerHuman extends PlayerController {
final CardView view = CardView.get(c);
tempShowCard(c);
boolean result = SGuiDialog.confirm(view, "Put " + view + " on the top or bottom of your library?", new String[]{"Top", "Bottom"});
final boolean result = getGui().confirm(view, "Put " + view + " on the top or bottom of your library?", new String[]{"Top", "Bottom"});
endTempShowCards();
return result;
@@ -621,22 +629,22 @@ public class PlayerControllerHuman extends PlayerController {
tempShowCards(cards);
switch (destinationZone) {
case Library:
choices = SGuiChoose.order("Choose order of cards to put into the library", "Closest to top", CardView.getCollection(cards), null);
choices = getGui().order("Choose order of cards to put into the library", "Closest to top", CardView.getCollection(cards), null);
break;
case Battlefield:
choices = SGuiChoose.order("Choose order of cards to put onto the battlefield", "Put first", CardView.getCollection(cards), null);
choices = getGui().order("Choose order of cards to put onto the battlefield", "Put first", CardView.getCollection(cards), null);
break;
case Graveyard:
choices = SGuiChoose.order("Choose order of cards to put into the graveyard", "Closest to bottom", CardView.getCollection(cards), null);
choices = getGui().order("Choose order of cards to put into the graveyard", "Closest to bottom", CardView.getCollection(cards), null);
break;
case PlanarDeck:
choices = SGuiChoose.order("Choose order of cards to put into the planar deck", "Closest to top", CardView.getCollection(cards), null);
choices = getGui().order("Choose order of cards to put into the planar deck", "Closest to top", CardView.getCollection(cards), null);
break;
case SchemeDeck:
choices = SGuiChoose.order("Choose order of cards to put into the scheme deck", "Closest to top", CardView.getCollection(cards), null);
choices = getGui().order("Choose order of cards to put into the scheme deck", "Closest to top", CardView.getCollection(cards), null);
break;
case Stack:
choices = SGuiChoose.order("Choose order of copies to cast", "Put first", CardView.getCollection(cards), null);
choices = getGui().order("Choose order of copies to cast", "Put first", CardView.getCollection(cards), null);
break;
default:
System.out.println("ZoneType " + destinationZone + " - Not Ordered");
@@ -651,7 +659,7 @@ public class PlayerControllerHuman extends PlayerController {
public CardCollectionView chooseCardsToDiscardFrom(Player p, SpellAbility sa, CardCollection valid, int min, int max) {
if (p != player) {
tempShowCards(valid);
final CardCollection choices = game.getCardList(SGuiChoose.many("Choose " + min + " card" + (min != 1 ? "s" : "") + " to discard",
final CardCollection choices = game.getCardList(getGui().many("Choose " + min + " card" + (min != 1 ? "s" : "") + " to discard",
"Discarded", min, min, CardView.getCollection(valid), null));
endTempShowCards();
return choices;
@@ -666,7 +674,7 @@ public class PlayerControllerHuman extends PlayerController {
@Override
public void playMiracle(final SpellAbility miracle, final Card card) {
final CardView view = CardView.get(card);
if (SGuiDialog.confirm(view, view + " - Drawn. Play for Miracle Cost?")) {
if (getGui().confirm(view, view + " - Drawn. Play for Miracle Cost?")) {
HumanPlay.playSpellAbility(this, player, miracle);
}
}
@@ -683,9 +691,9 @@ public class PlayerControllerHuman extends PlayerController {
cntChoice[i] = Integer.valueOf(i);
}
final Integer chosenAmount = SGuiChoose.one("Delve how many cards?", cntChoice);
final Integer chosenAmount = getGui().one("Delve how many cards?", cntChoice);
for (int i = 0; i < chosenAmount; i++) {
final CardView nowChosen = SGuiChoose.oneOrNone("Exile which card?", CardView.getCollection(grave));
final CardView nowChosen = getGui().oneOrNone("Exile which card?", CardView.getCollection(grave));
if (nowChosen == null) {
// User canceled,abort delving.
@@ -754,7 +762,7 @@ public class PlayerControllerHuman extends PlayerController {
Mana m = manaChoices.get(i);
options.add(String.format("%d. %s mana from %s", 1+i, MagicColor.toLongString(m.getColor()), m.getSourceCard()));
}
String chosen = SGuiChoose.one("Pay Mana from Mana Pool", options);
String chosen = getGui().one("Pay Mana from Mana Pool", options);
String idx = TextUtil.split(chosen, '.')[0];
return manaChoices.get(Integer.parseInt(idx)-1);
}
@@ -769,14 +777,14 @@ public class PlayerControllerHuman extends PlayerController {
Iterables.removeAll(types, invalidTypes);
}
if (isOptional) {
return SGuiChoose.oneOrNone("Choose a " + kindOfType.toLowerCase() + " type", types);
return getGui().oneOrNone("Choose a " + kindOfType.toLowerCase() + " type", types);
}
return SGuiChoose.one("Choose a " + kindOfType.toLowerCase() + " type", types);
return getGui().one("Choose a " + kindOfType.toLowerCase() + " type", types);
}
@Override
public Object vote(SpellAbility sa, String prompt, List<Object> options, ArrayListMultimap<Object, Player> votes) {
return SGuiChoose.one(prompt, options);
return getGui().one(prompt, options);
}
/* (non-Javadoc)
@@ -784,7 +792,7 @@ public class PlayerControllerHuman extends PlayerController {
*/
@Override
public boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, String question) {
return SGuiDialog.confirm(CardView.get(replacementEffect.getHostCard()), question);
return getGui().confirm(CardView.get(replacementEffect.getHostCard()), question);
}
@Override
@@ -795,7 +803,7 @@ public class PlayerControllerHuman extends PlayerController {
}
@Override
public void declareAttackers(Player attackingPlayer, Combat combat) {
public void declareAttackers(final Player attackingPlayer, final Combat combat) {
if (mayAutoPass()) {
if (CombatUtil.validateAttackers(combat)) {
return; //don't prompt to declare attackers if user chose to end the turn and not attacking is legal
@@ -814,41 +822,13 @@ public class PlayerControllerHuman extends PlayerController {
// This input should not modify combat object itself, but should return user choice
final InputBlock inpBlock = new InputBlock(this, defender, combat);
inpBlock.showAndWait();
updateAutoPassPrompt();
getGui().updateAutoPassPrompt();
}
public void updateAutoPassPrompt() {
if (mayAutoPass()) {
//allow user to cancel auto-pass
InputBase.cancelAwaitNextInput(); //don't overwrite prompt with awaiting opponent
PhaseType phase = getAutoPassUntilPhase();
MatchUtil.getController().showPromptMessage(player.getView(), "Yielding until " + (phase == PhaseType.CLEANUP ? "end of turn" : phase.nameForUi.toString()) +
".\nYou may cancel this yield to take an action.");
ButtonUtil.update(player.getView(), false, true, false);
}
}
@Override
public void autoPassUntilEndOfTurn() {
super.autoPassUntilEndOfTurn();
updateAutoPassPrompt();
}
@Override
public void autoPassCancel() {
if (getAutoPassUntilPhase() == null) { return; }
super.autoPassCancel();
//prevent prompt getting stuck on yielding message while actually waiting for next input opportunity
PlayerView playerView = getLocalPlayerView();
MatchUtil.getController().showPromptMessage(playerView, "");
ButtonUtil.update(playerView, false, false, false);
InputBase.awaitNextInput(this);
}
@Override
public List<SpellAbility> chooseSpellAbilityToPlay() {
MagicStack stack = game.getStack();
final MagicStack stack = game.getStack();
if (mayAutoPass()) {
//avoid prompting for input if current phase is set to be auto-passed
@@ -856,7 +836,7 @@ public class PlayerControllerHuman extends PlayerController {
int delay = 0;
if (stack.isEmpty()) {
//make sure to briefly pause at phases you're not set up to skip
if (!isUiSetToSkipPhase(game.getPhaseHandler().getPlayerTurn(), game.getPhaseHandler().getPhase())) {
if (!getGui().isUiSetToSkipPhase(game.getPhaseHandler().getPlayerTurn().getView(), game.getPhaseHandler().getPhase())) {
delay = FControlGamePlayback.phasesDelay;
}
}
@@ -876,13 +856,12 @@ public class PlayerControllerHuman extends PlayerController {
}
if (stack.isEmpty()) {
if (isUiSetToSkipPhase(game.getPhaseHandler().getPlayerTurn(), game.getPhaseHandler().getPhase())) {
if (getGui().isUiSetToSkipPhase(game.getPhaseHandler().getPlayerTurn().getView(), game.getPhaseHandler().getPhase())) {
return null; //avoid prompt for input if stack is empty and player is set to skip the current phase
}
}
else if (!game.getDisableAutoYields()) {
SpellAbility ability = stack.peekAbility();
if (ability != null && ability.isAbility() && shouldAutoYield(ability.toUnsuppressedString())) {
} else {
final SpellAbility ability = stack.peekAbility();
if (ability != null && ability.isAbility() && getGui().shouldAutoYield(ability.toUnsuppressedString())) {
//avoid prompt for input if top ability of stack is set to auto-yield
try {
Thread.sleep(FControlGamePlayback.resolveDelay);
@@ -894,7 +873,7 @@ public class PlayerControllerHuman extends PlayerController {
}
}
InputPassPriority defaultInput = new InputPassPriority(this, player);
final InputPassPriority defaultInput = new InputPassPriority(this);
defaultInput.showAndWait();
return defaultInput.getChosenSa();
}
@@ -946,7 +925,7 @@ public class PlayerControllerHuman extends PlayerController {
if (srcCards.isEmpty()) {
return result;
}
final List<CardView> chosen = SGuiChoose.many("Choose cards to activate from opening hand and their order", "Activate first", -1, CardView.getCollection(srcCards), null);
final List<CardView> chosen = getGui().many("Choose cards to activate from opening hand and their order", "Activate first", -1, CardView.getCollection(srcCards), null);
for (final CardView view : chosen) {
final Card c = game.getCard(view);
for (SpellAbility sa : usableFromOpeningHand) {
@@ -971,7 +950,7 @@ public class PlayerControllerHuman extends PlayerController {
case PlayOrDraw: labels = new String[]{"Play", "Draw"}; break;
default: labels = kindOfChoice.toString().split("Or");
}
return SGuiDialog.confirm(CardView.get(sa.getHostCard()), question, defaultVal == null || defaultVal.booleanValue(), labels);
return getGui().confirm(CardView.get(sa.getHostCard()), question, defaultVal == null || defaultVal.booleanValue(), labels);
}
@Override
@@ -981,13 +960,13 @@ public class PlayerControllerHuman extends PlayerController {
for (int i = 0; i < results.length; i++) {
strResults[i] = labelsSrc[results[i] ? 0 : 1];
}
return SGuiChoose.one(sa.getHostCard().getName() + " - Choose a result", strResults).equals(labelsSrc[0]);
return getGui().one(sa.getHostCard().getName() + " - Choose a result", strResults).equals(labelsSrc[0]);
}
@Override
public Card chooseProtectionShield(GameEntity entityBeingDamaged, List<String> options, Map<String, Card> choiceMap) {
String title = entityBeingDamaged + " - select which prevention shield to use";
return choiceMap.get(SGuiChoose.one(title, options));
return choiceMap.get(getGui().one(title, options));
}
@Override
@@ -998,12 +977,12 @@ public class PlayerControllerHuman extends PlayerController {
}
String counterChoiceTitle = "Choose a counter type on " + cardWithCounter;
final CounterType chosen = SGuiChoose.one(counterChoiceTitle, cardWithCounter.getCounters().keySet());
final CounterType chosen = getGui().one(counterChoiceTitle, cardWithCounter.getCounters().keySet());
String putOrRemoveTitle = "What to do with that '" + chosen.getName() + "' counter ";
final String putString = "Put another " + chosen.getName() + " counter on " + cardWithCounter;
final String removeString = "Remove a " + chosen.getName() + " counter from " + cardWithCounter;
final String addOrRemove = SGuiChoose.one(putOrRemoveTitle, new String[]{putString,removeString});
final String addOrRemove = getGui().one(putOrRemoveTitle, new String[]{putString,removeString});
return new ImmutablePair<CounterType,String>(chosen,addOrRemove);
}
@@ -1021,7 +1000,7 @@ public class PlayerControllerHuman extends PlayerController {
}
};
List<Pair<SpellAbilityStackInstance, GameObject>> chosen = SGuiChoose.getChoices(saSpellskite.getHostCard().getName(), 1, 1, allTargets, null, fnToString);
List<Pair<SpellAbilityStackInstance, GameObject>> chosen = getGui().getChoices(saSpellskite.getHostCard().getName(), 1, 1, allTargets, null, fnToString);
return Iterables.getFirst(chosen, null);
}
@@ -1031,7 +1010,7 @@ public class PlayerControllerHuman extends PlayerController {
if (sa != null && sa.isManaAbility()) {
game.getGameLog().add(GameLogEntryType.LAND, message);
} else {
SGuiDialog.message(message, sa == null || sa.getHostCard() == null ? "" : CardView.get(sa.getHostCard()).toString());
getGui().message(message, sa == null || sa.getHostCard() == null ? "" : CardView.get(sa.getHostCard()).toString());
}
}
@@ -1048,10 +1027,10 @@ public class PlayerControllerHuman extends PlayerController {
for (int i = 0; i < num; i++) {
AbilitySub a;
if (i < min) {
a = SGuiChoose.one(modeTitle, choices);
a = getGui().one(modeTitle, choices);
}
else {
a = SGuiChoose.oneOrNone(modeTitle, choices);
a = getGui().oneOrNone(modeTitle, choices);
}
if (a == null) {
break;
@@ -1065,7 +1044,7 @@ public class PlayerControllerHuman extends PlayerController {
@Override
public List<String> chooseColors(String message, SpellAbility sa, int min, int max, List<String> options) {
return SGuiChoose.getChoices(message, min, max, options);
return getGui().getChoices(message, min, max, options);
}
@Override
@@ -1099,9 +1078,9 @@ public class PlayerControllerHuman extends PlayerController {
colorNames[i++] = MagicColor.toLongString(b);
}
if (colorNames.length > 2) {
return MagicColor.fromName(SGuiChoose.one(message, colorNames));
return MagicColor.fromName(getGui().one(message, colorNames));
}
int idxChosen = SGuiDialog.confirm(CardView.get(c), message, colorNames) ? 0 : 1;
int idxChosen = getGui().confirm(CardView.get(c), message, colorNames) ? 0 : 1;
return MagicColor.fromName(colorNames[idxChosen]);
}
@@ -1110,7 +1089,7 @@ public class PlayerControllerHuman extends PlayerController {
Iterable<PaperCard> cardsFromDb = FModel.getMagicDb().getCommonCards().getUniqueCards();
List<PaperCard> cards = Lists.newArrayList(Iterables.filter(cardsFromDb, cpp));
Collections.sort(cards);
return SGuiChoose.one(message, cards);
return getGui().one(message, cards);
}
@Override
@@ -1118,7 +1097,7 @@ public class PlayerControllerHuman extends PlayerController {
if (options.size() <= 1) {
return Iterables.getFirst(options, null);
}
return SGuiChoose.one(prompt, options);
return getGui().one(prompt, options);
}
@Override
@@ -1133,12 +1112,12 @@ public class PlayerControllerHuman extends PlayerController {
if (possibleReplacers.size() == 1) {
return possibleReplacers.get(0);
}
return SGuiChoose.one(prompt, possibleReplacers);
return getGui().one(prompt, possibleReplacers);
}
@Override
public String chooseProtectionType(String string, SpellAbility sa, List<String> choices) {
return SGuiChoose.one(string, choices);
return getGui().one(string, choices);
}
@Override
@@ -1151,7 +1130,7 @@ public class PlayerControllerHuman extends PlayerController {
public void orderAndPlaySimultaneousSa(List<SpellAbility> activePlayerSAs) {
List<SpellAbility> orderedSAs = activePlayerSAs;
if (activePlayerSAs.size() > 1) { // give a dual list form to create instead of needing to do it one at a time
orderedSAs = SGuiChoose.order("Select order for Simultaneous Spell Abilities", "Resolve first", activePlayerSAs, null);
orderedSAs = getGui().order("Select order for Simultaneous Spell Abilities", "Resolve first", activePlayerSAs, null);
}
int size = orderedSAs.size();
for (int i = size - 1; i >= 0; i--) {
@@ -1199,7 +1178,7 @@ public class PlayerControllerHuman extends PlayerController {
final String p1Str = String.format("Pile 1 (%s cards)", pile1.size());
final String p2Str = String.format("Pile 2 (%s cards)", pile2.size());
final String[] possibleValues = { p1Str , p2Str };
return SGuiDialog.confirm(CardView.get(sa.getHostCard()), "Choose a Pile", possibleValues);
return getGui().confirm(CardView.get(sa.getHostCard()), "Choose a Pile", possibleValues);
}
tempShowCards(pile1);
@@ -1217,7 +1196,7 @@ public class PlayerControllerHuman extends PlayerController {
// make sure Pile 1 or Pile 2 is clicked on
boolean result;
while (true) {
final CardView chosen = SGuiChoose.one("Choose a pile", cards);
final CardView chosen = getGui().one("Choose a pile", cards);
if (chosen.equals(pileView1)) {
result = true;
break;
@@ -1235,7 +1214,7 @@ public class PlayerControllerHuman extends PlayerController {
@Override
public void revealAnte(String message, Multimap<Player, PaperCard> removedAnteCards) {
for (Player p : removedAnteCards.keySet()) {
SGuiChoose.reveal(message + " from " + Lang.getPossessedObject(MessageUtil.mayBeYou(player, p), "deck"), removedAnteCards.get(p));
getGui().reveal(message + " from " + Lang.getPossessedObject(MessageUtil.mayBeYou(player, p), "deck"), removedAnteCards.get(p));
}
}
@@ -1248,12 +1227,12 @@ public class PlayerControllerHuman extends PlayerController {
for (CardShields shield : c.getShields()) {
shields.add(shield);
}
return SGuiChoose.one("Choose a regeneration shield:", shields);
return getGui().one("Choose a regeneration shield:", shields);
}
@Override
public List<PaperCard> chooseCardsYouWonToAddToDeck(List<PaperCard> losses) {
return SGuiChoose.many("Select cards to add to your deck", "Add these to my deck", 0, losses.size(), losses, null);
return getGui().many("Select cards to add to your deck", "Add these to my deck", 0, losses.size(), losses, null);
}
@Override
@@ -1351,7 +1330,7 @@ public class PlayerControllerHuman extends PlayerController {
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
SOptionPane.showMessageDialog("Cannot pass priority at this time.");
getGui().message("Cannot pass priority at this time.");
}
});
return false;
@@ -1392,8 +1371,8 @@ public class PlayerControllerHuman extends PlayerController {
public boolean canPlayUnlimitedLands() {
return canPlayUnlimitedLands;
}
private DevModeCheats cheats;
public DevModeCheats cheat() {
private IDevModeCheats cheats;
public IDevModeCheats cheat() {
if (cheats == null) {
cheats = new DevModeCheats();
//TODO: In Network game, inform other players that this player is cheating
@@ -1403,25 +1382,37 @@ public class PlayerControllerHuman extends PlayerController {
public boolean hasCheated() {
return cheats != null;
}
public class DevModeCheats {
public class DevModeCheats implements IDevModeCheats {
private DevModeCheats() {
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#setCanPlayUnlimitedLands(boolean)
*/
@Override
public void setCanPlayUnlimitedLands(boolean canPlayUnlimitedLands0) {
canPlayUnlimitedLands = canPlayUnlimitedLands0;
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#setViewAllCards(boolean)
*/
@Override
public void setViewAllCards(final boolean canViewAll) {
mayLookAtAllCards = canViewAll;
for (final Player p : game.getPlayers()) {
MatchUtil.updateCards(CardView.getCollection(p.getAllCards()));
getGui().updateCards(CardView.getCollection(p.getAllCards()));
}
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#generateMana()
*/
@Override
public void generateMana() {
Player pPriority = game.getPhaseHandler().getPriorityPlayer();
if (pPriority == null) {
SGuiDialog.message("No player has priority at the moment, so mana cannot be added to their pool.");
getGui().message("No player has priority at the moment, so mana cannot be added to their pool.");
return;
}
@@ -1444,12 +1435,16 @@ public class PlayerControllerHuman extends PlayerController {
};
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#dumpGameState()
*/
@Override
public void dumpGameState() {
final GameState state = createGameStateObject();
try {
state.initFromGame(game);
File f = GuiBase.getInterface().getSaveFile(new File(ForgeConstants.USER_GAMES_DIR, "state.txt"));
if (f != null && (!f.exists() || SOptionPane.showConfirmDialog("Overwrite existing file?"))) {
if (f != null && (!f.exists() || getGui().showConfirmDialog("Overwrite existing file?"))) {
final BufferedWriter bw = new BufferedWriter(new FileWriter(f));
bw.write(state.toString());
bw.close();
@@ -1459,11 +1454,15 @@ public class PlayerControllerHuman extends PlayerController {
if (e.getMessage() != null) {
err += ": " + e.getMessage();
}
SOptionPane.showErrorDialog(err);
getGui().showErrorDialog(err);
e.printStackTrace();
}
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#setupGameState()
*/
@Override
public void setupGameState() {
File gamesDir = new File(ForgeConstants.USER_GAMES_DIR);
if (!gamesDir.exists()) { // if the directory does not exist, try to create it
@@ -1491,16 +1490,20 @@ public class PlayerControllerHuman extends PlayerController {
Player pPriority = game.getPhaseHandler().getPriorityPlayer();
if (pPriority == null) {
SGuiDialog.message("No player has priority at the moment, so game state cannot be setup.");
getGui().message("No player has priority at the moment, so game state cannot be setup.");
return;
}
state.applyToGame(game);
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#tutorForCard()
*/
@Override
public void tutorForCard() {
Player pPriority = game.getPhaseHandler().getPriorityPlayer();
if (pPriority == null) {
SGuiDialog.message("No player has priority at the moment, so their deck can't be tutored from.");
getGui().message("No player has priority at the moment, so their deck can't be tutored from.");
return;
}
@@ -1519,20 +1522,28 @@ public class PlayerControllerHuman extends PlayerController {
});
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#addCountersToPermanent()
*/
@Override
public void addCountersToPermanent() {
final CardCollectionView cards = game.getCardsIn(ZoneType.Battlefield);
final Card card = game.getCard(SGuiChoose.oneOrNone("Add counters to which card?", CardView.getCollection(cards)));
final Card card = game.getCard(getGui().oneOrNone("Add counters to which card?", CardView.getCollection(cards)));
if (card == null) { return; }
final CounterType counter = SGuiChoose.oneOrNone("Which type of counter?", CounterType.values());
final CounterType counter = getGui().oneOrNone("Which type of counter?", CounterType.values());
if (counter == null) { return; }
final Integer count = SGuiChoose.getInteger("How many counters?", 1, Integer.MAX_VALUE, 10);
final Integer count = getGui().getInteger("How many counters?", 1, Integer.MAX_VALUE, 10);
if (count == null) { return; }
card.addCounter(counter, count, false);
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#tapPermanents()
*/
@Override
public void tapPermanents() {
game.getAction().invoke(new Runnable() {
@Override
@@ -1551,6 +1562,10 @@ public class PlayerControllerHuman extends PlayerController {
});
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#untapPermanents()
*/
@Override
public void untapPermanents() {
game.getAction().invoke(new Runnable() {
@Override
@@ -1569,20 +1584,28 @@ public class PlayerControllerHuman extends PlayerController {
});
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#setPlayerLife()
*/
@Override
public void setPlayerLife() {
final Player player = game.getPlayer(SGuiChoose.oneOrNone("Set life for which player?", PlayerView.getCollection(game.getPlayers())));
final Player player = game.getPlayer(getGui().oneOrNone("Set life for which player?", PlayerView.getCollection(game.getPlayers())));
if (player == null) { return; }
final Integer life = SGuiChoose.getInteger("Set life to what?", 0);
final Integer life = getGui().getInteger("Set life to what?", 0);
if (life == null) { return; }
player.setLife(life, null);
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#winGame()
*/
@Override
public void winGame() {
Input input = inputQueue.getInput();
if (!(input instanceof InputPassPriority)) {
SOptionPane.showMessageDialog("You must have priority to use this feature.", "Win Game", SOptionPane.INFORMATION_ICON);
getGui().message("You must have priority to use this feature.", "Win Game");
return;
}
@@ -1599,8 +1622,12 @@ public class PlayerControllerHuman extends PlayerController {
input.selectButtonOK();
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#addCardToHand()
*/
@Override
public void addCardToHand() {
final Player p = game.getPlayer(SGuiChoose.oneOrNone("Put card in hand for which player?", PlayerView.getCollection(game.getPlayers())));
final Player p = game.getPlayer(getGui().oneOrNone("Put card in hand for which player?", PlayerView.getCollection(game.getPlayers())));
if (p == null) {
return;
}
@@ -1609,7 +1636,7 @@ public class PlayerControllerHuman extends PlayerController {
Collections.sort(cards);
// use standard forge's list selection dialog
final IPaperCard c = SGuiChoose.oneOrNone("Name the card", cards);
final IPaperCard c = getGui().oneOrNone("Name the card", cards);
if (c == null) {
return;
}
@@ -1619,8 +1646,12 @@ public class PlayerControllerHuman extends PlayerController {
}});
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#addCardToBattlefield()
*/
@Override
public void addCardToBattlefield() {
final Player p = game.getPlayer(SGuiChoose.oneOrNone("Put card in play for which player?", PlayerView.getCollection(game.getPlayers())));
final Player p = game.getPlayer(getGui().oneOrNone("Put card in play for which player?", PlayerView.getCollection(game.getPlayers())));
if (p == null) {
return;
}
@@ -1629,7 +1660,7 @@ public class PlayerControllerHuman extends PlayerController {
Collections.sort(cards);
// use standard forge's list selection dialog
final IPaperCard c = SGuiChoose.oneOrNone("Name the card", cards);
final IPaperCard c = getGui().oneOrNone("Name the card", cards);
if (c == null) {
return;
}
@@ -1654,7 +1685,7 @@ public class PlayerControllerHuman extends PlayerController {
sa = choices.iterator().next();
}
else {
sa = SGuiChoose.oneOrNone("Choose", (FCollection<SpellAbility>)choices);
sa = getGui().oneOrNone("Choose", (FCollection<SpellAbility>)choices);
}
if (sa == null) {
return; // happens if cancelled
@@ -1670,11 +1701,15 @@ public class PlayerControllerHuman extends PlayerController {
});
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#riggedPlanarRoll()
*/
@Override
public void riggedPlanarRoll() {
final Player player = game.getPlayer(SGuiChoose.oneOrNone("Which player should roll?", PlayerView.getCollection(game.getPlayers())));
final Player player = game.getPlayer(getGui().oneOrNone("Which player should roll?", PlayerView.getCollection(game.getPlayers())));
if (player == null) { return; }
final PlanarDice res = SGuiChoose.oneOrNone("Choose result", PlanarDice.values());
final PlanarDice res = getGui().oneOrNone("Choose result", PlanarDice.values());
if (res == null) { return; }
System.out.println("Rigging planar dice roll: " + res.toString());
@@ -1687,6 +1722,10 @@ public class PlayerControllerHuman extends PlayerController {
});
}
/* (non-Javadoc)
* @see forge.player.IDevModeCheats#planeswalkTo()
*/
@Override
public void planeswalkTo() {
if (!game.getRules().hasAppliedVariant(GameType.Planechase)) { return; }
final Player p = game.getPhaseHandler().getPlayerTurn();
@@ -1700,7 +1739,7 @@ public class PlayerControllerHuman extends PlayerController {
Collections.sort(allPlanars);
// use standard forge's list selection dialog
final IPaperCard c = SGuiChoose.oneOrNone("Name the card", allPlanars);
final IPaperCard c = getGui().oneOrNone("Name the card", allPlanars);
if (c == null) { return; }
final Card forgeCard = Card.fromPaperCard(c, p);
@@ -1714,4 +1753,36 @@ public class PlayerControllerHuman extends PlayerController {
});
}
}
@Override
public void concede() {
this.player.concede();
getGame().getAction().checkGameOverCondition();
}
public boolean mayAutoPass() {
return getGui().mayAutoPass(getLocalPlayerView());
}
public void autoPassUntilEndOfTurn() {
getGui().autoPassUntilEndOfTurn(getLocalPlayerView());
}
@Override
public void autoPassCancel() {
getGui().autoPassCancel(getLocalPlayerView());
}
public void awaitNextInput() {
getGui().awaitNextInput();
}
public void cancelAwaitNextInput() {
getGui().cancelAwaitNextInput();
}
@Override
public void nextGameDecision(final NextGameDecision decision) {
game.fireEvent(new UiEventNextGameDecision(this, decision));
}
@Override
public String getActivateDescription(final CardView card) {
return getInputProxy().getActivateAction(card);
}
}

View File

@@ -35,10 +35,8 @@ import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.match.MatchUtil;
import forge.match.input.InputSelectTargets;
import forge.util.Aggregates;
import forge.util.gui.SGuiChoose;
/**
* <p>
@@ -137,12 +135,12 @@ public class TargetSelection {
for (Card card : validTargets) {
playersWithValidTargets.put(PlayerView.get(card.getController()), null);
}
if (MatchUtil.getController().openZones(zone, playersWithValidTargets)) {
if (controller.getGui().openZones(zone, playersWithValidTargets)) {
InputSelectTargets inp = new InputSelectTargets(controller, validTargets, ability, mandatory);
inp.showAndWait();
choiceResult = !inp.hasCancelled();
bTargetingDone = inp.hasPressedOk();
MatchUtil.getController().restoreOldZones(playersWithValidTargets);
controller.getGui().restoreOldZones(playersWithValidTargets);
}
else {
// for every other case an all-purpose GuiChoose
@@ -206,10 +204,10 @@ public class TargetSelection {
Object chosen = null;
if (!choices.isEmpty() && mandatory) {
chosen = SGuiChoose.one(getTgt().getVTSelection(), choicesFiltered);
chosen = controller.getGui().one(getTgt().getVTSelection(), choicesFiltered);
}
else {
chosen = SGuiChoose.oneOrNone(getTgt().getVTSelection(), choicesFiltered);
chosen = controller.getGui().oneOrNone(getTgt().getVTSelection(), choicesFiltered);
}
if (chosen == null) {
return false;
@@ -258,7 +256,7 @@ public class TargetSelection {
return false;
}
else {
final Object madeChoice = SGuiChoose.oneOrNone(message, selectOptions);
final Object madeChoice = controller.getGui().oneOrNone(message, selectOptions);
if (madeChoice == null) {
return false;
}

View File

@@ -98,6 +98,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
ENFORCE_DECK_LEGALITY ("true"),
DEV_MODE_ENABLED ("false"),
DEV_WORKSHOP_SYNTAX ("false"),
DEV_LOG_ENTRY_TYPE (GameLogEntryType.DAMAGE.toString()),
DECK_DEFAULT_CARD_LIMIT ("4"),

View File

@@ -3,69 +3,56 @@ package forge.quest;
import java.util.ArrayList;
import java.util.List;
import forge.GuiBase;
import forge.deck.Deck;
import forge.deck.DeckGroup;
import forge.deck.DeckSection;
import forge.game.Game;
import forge.game.GameRules;
import forge.game.GameType;
import forge.game.Match;
import forge.game.player.RegisteredPlayer;
import forge.match.MatchUtil;
import forge.interfaces.IGuiGame;
import forge.match.HostedMatch;
import forge.model.FModel;
import forge.player.GamePlayerUtil;
import forge.properties.ForgePreferences.FPref;
import forge.util.storage.IStorage;
public class QuestDraftUtils {
private static List<DraftMatchup> matchups = new ArrayList<DraftMatchup>();
private static final List<DraftMatchup> matchups = new ArrayList<DraftMatchup>();
public static boolean matchInProgress = false;
public static boolean aiMatchInProgress = false;
private static boolean waitForUserInput = false;
public static void continueMatch(final Game lastGame) {
if (lastGame.getMatch().isMatchOver()) {
matchInProgress = false;
}
if (matchInProgress) {
MatchUtil.continueMatch();
}
else {
MatchUtil.endCurrentGame();
public static void completeDraft(final DeckGroup finishedDraft) {
final List<Deck> aiDecks = new ArrayList<Deck>(finishedDraft.getAiDecks());
finishedDraft.getAiDecks().clear();
for (int i = 0; i < aiDecks.size(); i++) {
final Deck oldDeck = aiDecks.get(i);
final Deck namedDeck = new Deck("AI Deck " + i);
namedDeck.putSection(DeckSection.Main, oldDeck.get(DeckSection.Main));
namedDeck.putSection(DeckSection.Sideboard, oldDeck.get(DeckSection.Sideboard));
finishedDraft.getAiDecks().add(namedDeck);
}
final IStorage<DeckGroup> draft = FModel.getQuest().getDraftDecks();
draft.add(finishedDraft);
FModel.getQuest().save();
}
public static void completeDraft(DeckGroup finishedDraft) {
List<Deck> aiDecks = new ArrayList<Deck>(finishedDraft.getAiDecks());
finishedDraft.getAiDecks().clear();
public static String getDeckLegality() {
final String message = GameType.QuestDraft.getDeckFormat().getDeckConformanceProblem(FModel.getQuest().getAssets().getDraftDeckStorage().get(QuestEventDraft.DECK_NAME).getHumanDeck());
if (message != null && FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
return message;
}
return null;
}
for (int i = 0; i < aiDecks.size(); i++) {
Deck oldDeck = aiDecks.get(i);
Deck namedDeck = new Deck("AI Deck " + i);
namedDeck.putSection(DeckSection.Main, oldDeck.get(DeckSection.Main));
namedDeck.putSection(DeckSection.Sideboard, oldDeck.get(DeckSection.Sideboard));
finishedDraft.getAiDecks().add(namedDeck);
}
IStorage<DeckGroup> draft = FModel.getQuest().getDraftDecks();
draft.add(finishedDraft);
FModel.getQuest().save();
}
public static String getDeckLegality() {
String message = GameType.QuestDraft.getDeckFormat().getDeckConformanceProblem(FModel.getQuest().getAssets().getDraftDeckStorage().get(QuestEventDraft.DECK_NAME).getHumanDeck());
if (message != null && FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) {
return message;
}
return null;
}
public static void startNextMatch() {
public static void startNextMatch(final IGuiGame gui) {
if (matchups.size() > 0) {
return;
}
@@ -127,7 +114,7 @@ public class QuestDraftUtils {
}
update();
update(gui);
}
@@ -148,9 +135,7 @@ public class QuestDraftUtils {
}
if (humanIndex > -1) {
matchup.hasHumanPlayer = true;
matchup.matchStarter.add(new RegisteredPlayer(decks.getHumanDeck()).setPlayer(GamePlayerUtil.getGuiPlayer()));
matchup.setHumanPlayer(new RegisteredPlayer(decks.getHumanDeck()).setPlayer(GamePlayerUtil.getGuiPlayer()));
int aiName = Integer.parseInt(draft.getStandings()[aiIndex]) - 1;
@@ -173,7 +158,7 @@ public class QuestDraftUtils {
matchups.add(matchup);
}
public static void update() {
public static void update(final IGuiGame gui) {
if (matchups.isEmpty()) {
if (!matchInProgress) {
aiMatchInProgress = false;
@@ -189,39 +174,45 @@ public class QuestDraftUtils {
return;
}
MatchUtil.getController().enableOverlay();
gui.enableOverlay();
DraftMatchup nextMatch = matchups.remove(0);
final DraftMatchup nextMatch = matchups.remove(0);
matchInProgress = true;
if (!nextMatch.hasHumanPlayer) {
MatchUtil.getController().disableOverlay();
if (nextMatch.hasHumanPlayer()) {
waitForUserInput = true;
aiMatchInProgress = false;
} else {
gui.disableOverlay();
waitForUserInput = false;
aiMatchInProgress = true;
}
else {
waitForUserInput = true;
aiMatchInProgress = false;
}
GameRules rules = new GameRules(GameType.QuestDraft);
final GameRules rules = new GameRules(GameType.QuestDraft);
rules.setPlayForAnte(false);
rules.setMatchAnteRarity(false);
rules.setGamesPerMatch(3);
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
rules.canCloneUseTargetsImage = FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE);
MatchUtil.getController().startNewMatch(new Match(rules, nextMatch.matchStarter));
final HostedMatch newMatch = GuiBase.getInterface().hostMatch();
newMatch.startMatch(rules, null, nextMatch.matchStarter, nextMatch.humanPlayer, gui);
}
public static void continueMatches() {
public static void continueMatches(final IGuiGame gui) {
waitForUserInput = false;
update();
update(gui);
}
private static class DraftMatchup {
private List<RegisteredPlayer> matchStarter = new ArrayList<RegisteredPlayer>();
private boolean hasHumanPlayer = false;
private static final class DraftMatchup {
private final List<RegisteredPlayer> matchStarter = new ArrayList<RegisteredPlayer>();
private RegisteredPlayer humanPlayer = null;
private void setHumanPlayer(final RegisteredPlayer humanPlayer) {
this.matchStarter.add(humanPlayer);
this.humanPlayer = humanPlayer;
}
private boolean hasHumanPlayer() {
return humanPlayer != null;
}
}
}

View File

@@ -1,22 +1,21 @@
package forge.quest;
import java.util.ArrayList;
import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import forge.card.CardRules;
import forge.item.IPaperCard;
import forge.item.InventoryItem;
import forge.item.PaperCard;
import forge.model.FModel;
import java.util.ArrayList;
import java.util.List;
/**
* TODO: Write javadoc for this type.
*
*/
public abstract class QuestRewardCard implements InventoryItem, IQuestRewardCard {
public abstract class QuestRewardCard implements IQuestRewardCard {
protected String buildDescription(final String [] input) {
final String defaultDescription = "a card";

View File

@@ -1,24 +1,23 @@
package forge.quest;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.item.InventoryItem;
import forge.item.PaperCard;
import forge.model.FModel;
import forge.util.ItemPool;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.item.PaperCard;
import forge.model.FModel;
import forge.util.ItemPool;
/**
* Resolves a card chooser InventoryItem into a CardPrinted.
* The initial version includes "duplicate", other type may be added later.
*
*/
public class QuestRewardCardChooser extends QuestRewardCard implements InventoryItem {
public class QuestRewardCardChooser extends QuestRewardCard {
/**
* Possible types for this object.
*/

View File

@@ -14,7 +14,7 @@ import java.util.List;
* Allows the player to choose a card from a predicate-filtered list of cards.
*
*/
public class QuestRewardCardFiltered extends QuestRewardCard implements IQuestRewardCard {
public class QuestRewardCardFiltered extends QuestRewardCard {
private final String description;
private final Predicate<PaperCard> predicates;

View File

@@ -17,6 +17,13 @@
*/
package forge.quest;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import com.google.common.collect.ImmutableMap;
import forge.FThreads;
import forge.GuiBase;
import forge.LobbyPlayer;
@@ -27,13 +34,13 @@ import forge.card.CardRules;
import forge.deck.Deck;
import forge.game.GameRules;
import forge.game.GameType;
import forge.game.Match;
import forge.game.card.Card;
import forge.game.player.RegisteredPlayer;
import forge.interfaces.IButton;
import forge.interfaces.IGuiGame;
import forge.item.IPaperCard;
import forge.item.PaperToken;
import forge.match.MatchUtil;
import forge.match.HostedMatch;
import forge.model.FModel;
import forge.player.GamePlayerUtil;
import forge.properties.ForgePreferences.FPref;
@@ -45,10 +52,6 @@ import forge.quest.data.QuestAssets;
import forge.quest.data.QuestPreferences.QPref;
import forge.util.gui.SGuiChoose;
import forge.util.gui.SOptionPane;
import org.apache.commons.lang3.tuple.ImmutablePair;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
@@ -540,8 +543,8 @@ public class QuestUtil {
forceAnte = qc.isForceAnte();
}
RegisteredPlayer humanStart = new RegisteredPlayer(getDeckForNewGame());
RegisteredPlayer aiStart = new RegisteredPlayer(event.getEventDeck());
final RegisteredPlayer humanStart = new RegisteredPlayer(getDeckForNewGame());
final RegisteredPlayer aiStart = new RegisteredPlayer(event.getEventDeck());
if (lifeHuman != null) {
humanStart.setStartingLife(lifeHuman);
@@ -555,34 +558,36 @@ public class QuestUtil {
aiStart.setCardsOnBattlefield(QuestUtil.getComputerStartingCards(event));
}
List<RegisteredPlayer> starter = new ArrayList<>();
final List<RegisteredPlayer> starter = new ArrayList<>();
starter.add(humanStart.setPlayer(GamePlayerUtil.getQuestPlayer()));
LobbyPlayer aiPlayer = GamePlayerUtil.createAiPlayer(event.getOpponent() == null ? event.getTitle() : event.getOpponent());
GuiBase.getInterface().setPlayerAvatar(aiPlayer, event);
final LobbyPlayer aiPlayer = GamePlayerUtil.createAiPlayer(event.getOpponent() == null ? event.getTitle() : event.getOpponent());
starter.add(aiStart.setPlayer(aiPlayer));
boolean useRandomFoil = FModel.getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL);
for(RegisteredPlayer rp : starter) {
final boolean useRandomFoil = FModel.getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL);
for (final RegisteredPlayer rp : starter) {
rp.setRandomFoil(useRandomFoil);
}
boolean useAnte = FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE);
boolean matchAnteRarity = FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE_MATCH_RARITY);
if(forceAnte != null)
final boolean matchAnteRarity = FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE_MATCH_RARITY);
if (forceAnte != null) {
useAnte = forceAnte;
GameRules rules = new GameRules(GameType.Quest);
}
final GameRules rules = new GameRules(GameType.Quest);
rules.setPlayForAnte(useAnte);
rules.setMatchAnteRarity(matchAnteRarity);
rules.setGamesPerMatch(qData.getCharmState() ? 5 : 3);
rules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN));
rules.canCloneUseTargetsImage = FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE);
final Match mc = new Match(rules, starter);
final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch();
final IGuiGame gui = GuiBase.getInterface().getNewGuiGame();
FThreads.invokeInEdtNowOrLater(new Runnable(){
@Override
public void run() {
MatchUtil.startGame(mc);
hostedMatch.startMatch(rules, null, starter, ImmutableMap.of(humanStart, gui));
}
});
gui.setPlayerAvatar(aiPlayer, event);
}
private static Deck getDeckForNewGame() {

View File

@@ -361,8 +361,7 @@ public class QuestDataIO {
}
@SuppressWarnings("unchecked")
private static <T> T readAsset(final XStream xs, final Document doc, final String tagName, final Class<T> clasz)
throws IllegalAccessException, NoSuchFieldException {
private static <T> T readAsset(final XStream xs, final Document doc, final String tagName, final Class<T> clasz) {
final NodeList nn = doc.getElementsByTagName(tagName);
final Node n = nn.item(0);

View File

@@ -6,6 +6,7 @@ import forge.LobbyPlayer;
import forge.events.IUiEventVisitor;
import forge.events.UiEventAttackerDeclared;
import forge.events.UiEventBlockerAssigned;
import forge.events.UiEventNextGameDecision;
import forge.game.card.Card;
import forge.game.event.GameEvent;
import forge.game.event.GameEventBlockersDeclared;
@@ -214,6 +215,10 @@ public class EventVisualizer extends IGameEventVisitor.Base<SoundEffectType> imp
return null;
}
@Override
public SoundEffectType visit(UiEventNextGameDecision event) {
return null;
}
@Override
public SoundEffectType visit(GameEventCardPhased event) {
return SoundEffectType.Phasing;
}

View File

@@ -14,9 +14,10 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.GuiBase;
import forge.game.card.CardView;
public class SGuiChoose {
public static final String[] defaultConfirmOptions = { "Yes", "No" };
/**
* Convenience for getChoices(message, 0, 1, choices).
*
@@ -191,18 +192,18 @@ public class SGuiChoose {
return GuiBase.getInterface().getChoices(message, min, max, choices, selected, display);
}
public static <T> List<T> many(final String title, final String topCaption, int cnt, final List<T> sourceChoices, final CardView referenceCard) {
return many(title, topCaption, cnt, cnt, sourceChoices, referenceCard);
public static <T> List<T> many(final String title, final String topCaption, int cnt, final List<T> sourceChoices) {
return many(title, topCaption, cnt, cnt, sourceChoices);
}
public static <T> List<T> many(final String title, final String topCaption, int min, int max, final List<T> sourceChoices, final CardView referenceCard) {
public static <T> List<T> many(final String title, final String topCaption, int min, int max, final List<T> sourceChoices) {
int m2 = min >= 0 ? sourceChoices.size() - min : -1;
int m1 = max >= 0 ? sourceChoices.size() - max : -1;
return order(title, topCaption, m1, m2, sourceChoices, null, referenceCard, false);
return order(title, topCaption, m1, m2, sourceChoices, null);
}
public static <T> List<T> order(final String title, final String top, final List<T> sourceChoices, final CardView referenceCard) {
return order(title, top, 0, 0, sourceChoices, null, referenceCard, false);
public static <T> List<T> order(final String title, final String top, final List<T> sourceChoices) {
return order(title, top, 0, 0, sourceChoices, null);
}
/**
@@ -216,17 +217,17 @@ public class SGuiChoose {
* @return A shallow copy of the list of objects, with newItem inserted.
*/
public static <T> List<T> insertInList(final String title, final T newItem, final List<T> oldItems) {
final T placeAfter = oneOrNone(title, oldItems);
final int indexAfter = (placeAfter == null ? 0 : oldItems.indexOf(placeAfter) + 1);
final List<T> result = Lists.newArrayListWithCapacity(oldItems.size() + 1);
result.addAll(oldItems);
result.add(indexAfter, newItem);
return result;
final T placeAfter = oneOrNone(title, oldItems);
final int indexAfter = (placeAfter == null ? 0 : oldItems.indexOf(placeAfter) + 1);
final List<T> result = Lists.newArrayListWithCapacity(oldItems.size() + 1);
result.addAll(oldItems);
result.add(indexAfter, newItem);
return result;
}
private static <T> List<T> order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax,
final List<T> sourceChoices, final List<T> destChoices, final CardView referenceCard, final boolean sideboardingMode) {
return GuiBase.getInterface().order(title, top, remainingObjectsMin, remainingObjectsMax, sourceChoices, destChoices, referenceCard, sideboardingMode);
final List<T> sourceChoices, final List<T> destChoices) {
return GuiBase.getInterface().order(title, top, remainingObjectsMin, remainingObjectsMax, sourceChoices, destChoices);
}
// If comparer is NULL, T has to be comparable. Otherwise you'll get an exception from inside the Arrays.sort() routine

View File

@@ -1,39 +0,0 @@
package forge.util.gui;
import org.apache.commons.lang3.StringUtils;
import forge.game.card.CardView;
/**
* Holds player interactions using standard windows
*
*/
public class SGuiDialog {
private static final String[] defaultConfirmOptions = { "Yes", "No" };
public static boolean confirm(final CardView c, final String question) {
return SGuiDialog.confirm(c, question, true, null);
}
public static boolean confirm(final CardView c, final String question, final boolean defaultChoice) {
return SGuiDialog.confirm(c, question, defaultChoice, null);
}
public static boolean confirm(final CardView c, final String question, String[] options) {
return SGuiDialog.confirm(c, question, true, options);
}
public static boolean confirm(final CardView c, final String question, final boolean defaultIsYes, final String[] options) {
final String title = c == null ? "Question" : c + " - Ability";
String questionToUse = StringUtils.isBlank(question) ? "Activate card's ability?" : question;
String[] opts = options == null ? defaultConfirmOptions : options;
int answer = SOptionPane.showCardOptionDialog(c, questionToUse, title, SOptionPane.QUESTION_ICON, opts, defaultIsYes ? 0 : 1);
return answer == 0;
}
public static void message(final String message) {
message(message, "Forge");
}
public static void message(final String message, final String title) {
SOptionPane.showMessageDialog(message, title, null);
}
}

View File

@@ -2,7 +2,6 @@ package forge.util.gui;
import forge.GuiBase;
import forge.assets.FSkinProp;
import forge.game.card.CardView;
public class SOptionPane {
public static final FSkinProp QUESTION_ICON = FSkinProp.ICO_QUESTION;
@@ -60,10 +59,6 @@ public class SOptionPane {
return GuiBase.getInterface().showOptionDialog(message, title, icon, options, defaultOption);
}
public static int showCardOptionDialog(CardView card, String message, String title, FSkinProp icon, String[] options, int defaultOption) {
return GuiBase.getInterface().showCardOptionDialog(card, message, title, icon, options, defaultOption);
}
public static String showInputDialog(String message, String title) {
return showInputDialog(message, title, null, "", null);
}