Flesh out PlayerControllerHuman

This commit is contained in:
drdev
2014-03-09 20:44:44 +00:00
parent c5ededc74b
commit 3ce0c595ce
11 changed files with 2384 additions and 328 deletions

4
.gitattributes vendored
View File

@@ -16053,6 +16053,7 @@ forge-m-base/src/forge/player/HumanPlay.java -text
forge-m-base/src/forge/player/HumanPlaySpellAbility.java -text forge-m-base/src/forge/player/HumanPlaySpellAbility.java -text
forge-m-base/src/forge/player/LobbyPlayerHuman.java -text forge-m-base/src/forge/player/LobbyPlayerHuman.java -text
forge-m-base/src/forge/player/PlayerControllerHuman.java -text forge-m-base/src/forge/player/PlayerControllerHuman.java -text
forge-m-base/src/forge/player/TargetSelection.java -text
forge-m-base/src/forge/screens/FScreen.java -text forge-m-base/src/forge/screens/FScreen.java -text
forge-m-base/src/forge/screens/LaunchScreen.java -text forge-m-base/src/forge/screens/LaunchScreen.java -text
forge-m-base/src/forge/screens/SplashScreen.java -text forge-m-base/src/forge/screens/SplashScreen.java -text
@@ -16092,6 +16093,7 @@ forge-m-base/src/forge/screens/match/input/InputSelectManyBase.java -text
forge-m-base/src/forge/screens/match/input/InputSelectTargets.java -text forge-m-base/src/forge/screens/match/input/InputSelectTargets.java -text
forge-m-base/src/forge/screens/match/input/InputSynchronized.java -text forge-m-base/src/forge/screens/match/input/InputSynchronized.java -text
forge-m-base/src/forge/screens/match/input/InputSyncronizedBase.java -text forge-m-base/src/forge/screens/match/input/InputSyncronizedBase.java -text
forge-m-base/src/forge/screens/match/views/VAssignDamage.java -text
forge-m-base/src/forge/screens/match/views/VAvatar.java -text forge-m-base/src/forge/screens/match/views/VAvatar.java -text
forge-m-base/src/forge/screens/match/views/VField.java -text forge-m-base/src/forge/screens/match/views/VField.java -text
forge-m-base/src/forge/screens/match/views/VGameDetails.java -text forge-m-base/src/forge/screens/match/views/VGameDetails.java -text
@@ -16121,6 +16123,8 @@ forge-m-base/src/forge/utils/Constants.java -text
forge-m-base/src/forge/utils/Evaluator.java -text forge-m-base/src/forge/utils/Evaluator.java -text
forge-m-base/src/forge/utils/ForgePreferences.java -text forge-m-base/src/forge/utils/ForgePreferences.java -text
forge-m-base/src/forge/utils/ForgeProfileProperties.java -text forge-m-base/src/forge/utils/ForgeProfileProperties.java -text
forge-m-base/src/forge/utils/GuiDisplayUtil.java -text
forge-m-base/src/forge/utils/GuiUtils.java -text
forge-m-base/src/forge/utils/Preferences.java -text forge-m-base/src/forge/utils/Preferences.java -text
forge-m-base/src/forge/utils/PreferencesStore.java -text forge-m-base/src/forge/utils/PreferencesStore.java -text
forge-m-base/src/forge/utils/Utils.java -text forge-m-base/src/forge/utils/Utils.java -text

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,387 @@
/*
* 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.player;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import forge.game.Game;
import forge.game.GameObject;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityStackInstance;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.screens.match.input.InputSelectTargets;
import forge.toolbox.GuiChoose;
import forge.util.Aggregates;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* Target_Selection class.
* </p>
*
* @author Forge
* @version $Id: TargetSelection.java 25112 2014-03-09 10:18:52Z swordshine $
*/
public class TargetSelection {
private final SpellAbility ability;
public TargetSelection(final SpellAbility sa) {
this.ability = sa;
}
private final TargetRestrictions getTgt() {
return this.ability.getTargetRestrictions();
}
private boolean bTargetingDone = false;
/**
* <p>
* resetTargets.
* </p>
*/
public final boolean chooseTargets(Integer numTargets) {
TargetRestrictions tgt = getTgt();
final boolean canTarget = tgt != null && tgt.doesTarget();
if (!canTarget) {
throw new RuntimeException("TargetSelection.chooseTargets called for ability that does not target - " + ability);
}
// Number of targets is explicitly set only if spell is being redirected (ex. Swerve or Redirect)
final int minTargets = numTargets != null ? numTargets.intValue() : tgt.getMinTargets(ability.getHostCard(), ability);
final int maxTargets = numTargets != null ? numTargets.intValue() : tgt.getMaxTargets(ability.getHostCard(), ability);
final int numTargeted = ability.getTargets().getNumTargeted();
boolean hasEnoughTargets = minTargets == 0 || numTargeted >= minTargets;
boolean hasAllTargets = numTargeted == maxTargets && maxTargets > 0;
if (maxTargets == 0) return true;
// if not enough targets chosen, cancel Ability
if (this.bTargetingDone && !hasEnoughTargets) {
return false;
}
if (this.bTargetingDone && hasEnoughTargets || hasAllTargets || tgt.isDividedAsYouChoose() && tgt.getStillToDivide() == 0) {
return true;
}
if (!tgt.hasCandidates(this.ability, true) && !hasEnoughTargets) {
// Cancel ability if there aren't any valid Candidates
return false;
}
final List<ZoneType> zone = tgt.getZone();
final boolean mandatory = tgt.getMandatory() && tgt.hasCandidates(this.ability, true);
final boolean choiceResult;
final boolean random = tgt.isRandomTarget();
if (random) {
List<GameObject> candidates = tgt.getAllCandidates(this.ability, true);
GameObject choice = Aggregates.random(candidates);
return ability.getTargets().add(choice);
}
else if (zone.size() == 1 && zone.get(0) == ZoneType.Stack) {
// If Zone is Stack, the choices are handled slightly differently.
// Handle everything inside function due to interaction with StackInstance
return this.chooseCardFromStack(mandatory);
}
else {
List<Card> validTargets = this.getValidCardsToTarget();
if (zone.size() == 1 && (zone.get(0) == ZoneType.Battlefield || zone.get(0) == ZoneType.Hand)) {
InputSelectTargets inp = new InputSelectTargets(validTargets, ability, mandatory);
inp.showAndWait();
choiceResult = !inp.hasCancelled();
bTargetingDone = inp.hasPressedOk();
} else {
// for every other case an all-purpose GuiChoose
choiceResult = this.chooseCardFromList(validTargets, true, mandatory);
}
}
// some inputs choose cards one-by-one and need to be called again
return choiceResult && chooseTargets(numTargets);
}
// these have been copied over from CardFactoryUtil as they need two extra
// parameters for target selection.
// however, due to the changes necessary for SA_Requirements this is much
// different than the original
/**
* <p>
* chooseValidInput.
* </p>
* @return
*/
private final List<Card> getValidCardsToTarget() {
final TargetRestrictions tgt = this.getTgt();
final Game game = ability.getActivatingPlayer().getGame();
final List<ZoneType> zone = tgt.getZone();
final boolean canTgtStack = zone.contains(ZoneType.Stack);
List<Card> validCards = CardLists.getValidCards(game.getCardsIn(zone), tgt.getValidTgts(), this.ability.getActivatingPlayer(), this.ability.getHostCard());
List<Card> choices = CardLists.getTargetableCards(validCards, this.ability);
if (canTgtStack) {
// Since getTargetableCards doesn't have additional checks if one of the Zones is stack
// Remove the activating card from targeting itself if its on the Stack
Card activatingCard = ability.getHostCard();
if (activatingCard.isInZone(ZoneType.Stack)) {
choices.remove(ability.getHostCard());
}
}
List<GameObject> targetedObjects = this.ability.getUniqueTargets();
if (tgt.isUniqueTargets()) {
for (final Object o : targetedObjects) {
if ((o instanceof Card) && targetedObjects.contains(o)) {
choices.remove(o);
}
}
}
// Remove cards already targeted
final List<Card> targeted = Lists.newArrayList(ability.getTargets().getTargetCards());
for (final Card c : targeted) {
if (choices.contains(c)) {
choices.remove(c);
}
}
// If all cards (including subability targets) must have the same controller
if (tgt.isSameController() && !targetedObjects.isEmpty()) {
final List<Card> list = new ArrayList<Card>();
for (final Object o : targetedObjects) {
if (o instanceof Card) {
list.add((Card) o);
}
}
if (!list.isEmpty()) {
final Card card = list.get(0);
choices = CardLists.filter(choices, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.sharesControllerWith(card);
}
});
}
}
// If second target has properties related to the first
if (tgt.getRelatedProperty() != null && !targetedObjects.isEmpty()) {
final List<Card> list = new ArrayList<Card>();
final String related = tgt.getRelatedProperty();
for (final Object o : targetedObjects) {
if (o instanceof Card) {
list.add((Card) o);
}
}
if (!list.isEmpty()) {
final Card card = list.get(0);
if ("LEPower".equals(related)) {
choices = CardLists.filter(choices, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.getNetAttack() <= card.getNetAttack();
}
});
}
if ("LECMC".equals(related)) {
choices = CardLists.filter(choices, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.getCMC() <= card.getCMC();
}
});
}
}
}
// If all cards must be from the same zone
if (tgt.isSingleZone() && !targeted.isEmpty()) {
choices = CardLists.filterControlledBy(choices, targeted.get(0).getController());
}
// If all cards must be from different zones
if (tgt.isDifferentZone() && !targeted.isEmpty()) {
choices = CardLists.filterControlledBy(choices, targeted.get(0).getController().getOpponent());
}
// If all cards must have different controllers
if (tgt.isDifferentControllers() && !targeted.isEmpty()) {
final List<Player> availableControllers = new ArrayList<Player>(game.getPlayers());
for (int i = 0; i < targeted.size(); i++) {
availableControllers.remove(targeted.get(i).getController());
}
choices = CardLists.filterControlledBy(choices, availableControllers);
}
// If the cards can't share a creature type
if (tgt.isWithoutSameCreatureType() && !targeted.isEmpty()) {
final Card card = targeted.get(0);
choices = CardLists.filter(choices, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return !c.sharesCreatureTypeWith(card);
}
});
}
return choices;
}
/**
* <p>
* chooseCardFromList.
* </p>
*
* @param choices
* a {@link forge.CardList} object.
* @param targeted
* a boolean.
* @param mandatory
* a boolean.
*/
private final boolean chooseCardFromList(final List<Card> choices, final boolean targeted, final boolean mandatory) {
// Send in a list of valid cards, and popup a choice box to target
final Game game = ability.getActivatingPlayer().getGame();
final List<Card> crdsBattle = new ArrayList<Card>();
final List<Card> crdsExile = new ArrayList<Card>();
final List<Card> crdsGrave = new ArrayList<Card>();
final List<Card> crdsLibrary = new ArrayList<Card>();
final List<Card> crdsStack = new ArrayList<Card>();
final List<Card> crdsAnte = new ArrayList<Card>();
for (final Card inZone : choices) {
Zone zz = game.getZoneOf(inZone);
if (zz.is(ZoneType.Battlefield)) crdsBattle.add(inZone);
else if (zz.is(ZoneType.Exile)) crdsExile.add(inZone);
else if (zz.is(ZoneType.Graveyard)) crdsGrave.add(inZone);
else if (zz.is(ZoneType.Library)) crdsLibrary.add(inZone);
else if (zz.is(ZoneType.Stack)) crdsStack.add(inZone);
else if (zz.is(ZoneType.Ante)) crdsAnte.add(inZone);
}
List<Object> choicesFiltered = new ArrayList<Object>();
if (!crdsBattle.isEmpty()) {
choicesFiltered.add("--CARDS ON BATTLEFIELD:--");
choicesFiltered.addAll(crdsBattle);
}
if (!crdsExile.isEmpty()) {
choicesFiltered.add("--CARDS IN EXILE:--");
choicesFiltered.addAll(crdsExile);
}
if (!crdsGrave.isEmpty()) {
choicesFiltered.add("--CARDS IN GRAVEYARD:--");
choicesFiltered.addAll(crdsGrave);
}
if (!crdsLibrary.isEmpty()) {
choicesFiltered.add("--CARDS IN LIBRARY:--");
choicesFiltered.addAll(crdsLibrary);
}
if (!crdsStack.isEmpty()) {
choicesFiltered.add("--CARDS IN STACK:--");
choicesFiltered.addAll(crdsStack);
}
if (!crdsAnte.isEmpty()) {
choicesFiltered.add("--CARDS IN ANTE:--");
choicesFiltered.addAll(crdsAnte);
}
final String msgDone = "[FINISH TARGETING]";
if (this.getTgt().isMinTargetsChosen(this.ability.getHostCard(), this.ability)) {
// is there a more elegant way of doing this?
choicesFiltered.add(msgDone);
}
Object chosen = null;
if (!choices.isEmpty() && mandatory) {
chosen = GuiChoose.one(getTgt().getVTSelection(), choicesFiltered);
}
else {
chosen = GuiChoose.oneOrNone(getTgt().getVTSelection(), choicesFiltered);
}
if (chosen == null) {
return false;
}
if (msgDone.equals(chosen)) {
bTargetingDone = true;
return true;
}
if (chosen instanceof Card)
ability.getTargets().add((Card)chosen);
return true;
}
/**
* <p>
* chooseCardFromStack.
* </p>
*
* @param mandatory
* a boolean.
*/
private final boolean chooseCardFromStack(final boolean mandatory) {
final TargetRestrictions tgt = this.getTgt();
final String message = tgt.getVTSelection();
// Find what's targetable, then allow human to choose
final List<Object> selectOptions = new ArrayList<Object>();
final Game game = ability.getActivatingPlayer().getGame();
for (SpellAbilityStackInstance si : game.getStack()) {
SpellAbility abilityOnStack = si.getSpellAbility();
if (ability.equals(abilityOnStack)) {
// By peeking at stack item, target is set to its SI state. So set it back before adding targets
ability.resetTargets();
}
else if (ability.canTargetSpellAbility(abilityOnStack)) {
selectOptions.add(abilityOnStack);
}
}
while(!bTargetingDone) {
if (tgt.isMaxTargetsChosen(this.ability.getHostCard(), this.ability)) {
bTargetingDone = true;
return true;
}
if (!selectOptions.contains("[FINISH TARGETING]") && tgt.isMinTargetsChosen(this.ability.getHostCard(), this.ability)) {
selectOptions.add("[FINISH TARGETING]");
}
if (selectOptions.isEmpty()) {
// Not enough targets, cancel targeting
return false;
} else {
final Object madeChoice = GuiChoose.oneOrNone(message, selectOptions);
if (madeChoice == null) {
return false;
}
if (madeChoice instanceof SpellAbility) {
ability.getTargets().add((SpellAbility)madeChoice);
} else // 'FINISH TARGETING' chosen
bTargetingDone = true;
}
}
return true;
}
}

View File

@@ -1,8 +1,10 @@
package forge.screens.match; package forge.screens.match;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
@@ -13,6 +15,7 @@ import com.google.common.eventbus.Subscribe;
import forge.FThreads; import forge.FThreads;
import forge.Forge; import forge.Forge;
import forge.game.Game; import forge.game.Game;
import forge.game.GameEntity;
import forge.game.Match; import forge.game.Match;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.combat.Combat; import forge.game.combat.Combat;
@@ -28,8 +31,10 @@ import forge.screens.match.events.UiEvent;
import forge.screens.match.events.UiEventAttackerDeclared; import forge.screens.match.events.UiEventAttackerDeclared;
import forge.screens.match.events.UiEventBlockerAssigned; import forge.screens.match.events.UiEventBlockerAssigned;
import forge.screens.match.input.InputQueue; import forge.screens.match.input.InputQueue;
import forge.screens.match.views.VAssignDamage;
import forge.screens.match.views.VPhaseIndicator.PhaseLabel; import forge.screens.match.views.VPhaseIndicator.PhaseLabel;
import forge.screens.match.views.VPlayerPanel; import forge.screens.match.views.VPlayerPanel;
import forge.toolbox.FCardPanel;
import forge.utils.ForgePreferences.FPref; import forge.utils.ForgePreferences.FPref;
public class FControl { public class FControl {
@@ -92,12 +97,12 @@ public class FControl {
// It's important to run match in a different thread to allow GUI inputs to be invoked from inside game. // 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 is set on pause while gui player takes decisions
/*game.getAction().invoke(new Runnable() { game.getAction().invoke(new Runnable() {
@Override @Override
public void run() { public void run() {
match.startGame(game); match.startGame(game);
} }
});*/ });
} }
public static Game getGame() { public static Game getGame() {
@@ -187,6 +192,25 @@ public class FControl {
return view.getPlayerPanels().get(p); return view.getPlayerPanels().get(p);
} }
public static void highlightCard(final Card c) {
for (VPlayerPanel playerPanel : FControl.getView().getPlayerPanels().values()) {
for (FCardPanel p : playerPanel.getField().getCardPanels()) {
if (p.getCard().equals(c)) {
p.setHighlighted(true);
return;
}
}
}
}
public static void clearCardHighlights() {
for (VPlayerPanel playerPanel : FControl.getView().getPlayerPanels().values()) {
for (FCardPanel p : playerPanel.getField().getCardPanels()) {
p.setHighlighted(false);
}
}
}
public static boolean mayShowCard(Card c) { public static boolean mayShowCard(Card c) {
return true;// game == null || !gameHasHumanPlayer || c.canBeShownTo(getCurrentPlayer()); return true;// game == null || !gameHasHumanPlayer || c.canBeShownTo(getCurrentPlayer());
} }
@@ -212,7 +236,31 @@ public class FControl {
} }
CCombat.SINGLETON_INSTANCE.setModel(combat); CCombat.SINGLETON_INSTANCE.setModel(combat);
CCombat.SINGLETON_INSTANCE.update();*/ CCombat.SINGLETON_INSTANCE.update();*/
} // showBlockers() }
@SuppressWarnings("unchecked")
public static Map<Card, Integer> getDamageToAssign(final Card attacker, final List<Card> blockers, final int damage, final GameEntity defender, final boolean overrideOrder) {
if (damage <= 0) {
return new HashMap<Card, Integer>();
}
// If the first blocker can absorb all of the damage, don't show the Assign Damage Frame
Card firstBlocker = blockers.get(0);
if (!overrideOrder && !attacker.hasKeyword("Deathtouch") && firstBlocker.getLethalDamage() >= damage) {
Map<Card, Integer> res = new HashMap<Card, Integer>();
res.put(firstBlocker, damage);
return res;
}
final Object[] result = { null }; // how else can I extract a value from EDT thread?
FThreads.invokeInEdtAndWait(new Runnable() {
@Override
public void run() {
VAssignDamage v = new VAssignDamage(attacker, blockers, damage, defender, overrideOrder);
result[0] = v.getDamageMap();
}});
return (Map<Card, Integer>)result[0];
}
private static Set<Player> highlightedPlayers = new HashSet<Player>(); private static Set<Player> highlightedPlayers = new HashSet<Player>();
public static void setHighlighted(Player ge, boolean b) { public static void setHighlighted(Player ge, boolean b) {

View File

@@ -0,0 +1,438 @@
/*
* 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.screens.match.views;
import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.toolbox.FButton;
import forge.toolbox.FCardPanel;
import forge.toolbox.FLabel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Assembles Swing components of assign damage dialog.
*
* This needs a JDialog to maintain a modal state.
* Without the modal state, the PhaseHandler automatically
* moves forward to phase Main2 without assigning damage.
*
* <br><br><i>(V at beginning of class name denotes a view class.)</i>
*/
public class VAssignDamage {
// Width and height of blocker dialog
private final int wDlg = 700;
private final int hDlg = 500;
//private final FDialog dlg = new FDialog();
// Damage storage
private final int totalDamageToAssign;
private boolean attackerHasDeathtouch = false;
private boolean attackerHasTrample = false;
private boolean attackerHasInfect = false;
private boolean overrideCombatantOrder = false;
private final GameEntity defender;
private final FLabel lblTotalDamage = new FLabel.Builder().text("Available damage points: Unknown").build();
private final FLabel lblAssignRemaining = new FLabel.Builder().text("Distribute the remaining damage points among lethally wounded entities").build();
// Label Buttons
private final FButton btnOK = new FButton("OK");
private final FButton btnReset = new FButton("Reset");
private final FButton btnAuto = new FButton("Auto");
private static class DamageTarget {
public final Card card;
public final FLabel label;
public int damage;
public DamageTarget(Card entity0, FLabel lbl) {
card = entity0;
label = lbl;
}
}
// Indexes of defenders correspond to their indexes in the damage list and labels.
private final List<DamageTarget> defenders = new ArrayList<DamageTarget>(); // NULL in this map means defender
private final Map<Card, DamageTarget> damage = new HashMap<Card, DamageTarget>(); // NULL in this map means defender
private boolean canAssignTo(Card card) {
for(DamageTarget dt : defenders) {
if (dt.card == card) return true;
if (getDamageToKill(dt.card) > dt.damage)
return false;
}
throw new RuntimeException("Asking to assign damage to object which is not present in defenders list");
}
// Mouse actions
/*private final MouseAdapter mad = new MouseAdapter() {
@Override
public void pressed(float x, float y) {
Card source = ((FCardPanel) evt.getSource()).getCard();
if (!damage.containsKey(source)) source = null; // to get player instead of fake card
FSkin.Colors brdrColor = VAssignDamage.this.canAssignTo(source) ? FSkin.Colors.CLR_ACTIVE : FSkin.Colors.CLR_INACTIVE;
((FCardPanel) evt.getSource()).setBorder(new FSkin.LineSkinBorder(FSkin.getColor(brdrColor), 2));
}
@Override
public void released(float x, float y) {
//((FCardPanel) evt.getSource()).setBorder((Border)null);
}
@Override
public void tap(float x, float y, int count) {
Card source = ((FCardPanel) evt.getSource()).getCard(); // will be NULL for player
boolean meta = evt.isControlDown();
boolean isLMB = SwingUtilities.isLeftMouseButton(evt);
boolean isRMB = SwingUtilities.isRightMouseButton(evt);
if (isLMB || isRMB)
assignDamageTo(source, meta, isLMB);
}
};*/
/** Constructor.
*
* @param attacker0 {@link forge.game.card.Card}
* @param defenderCards List<{@link forge.game.card.Card}>
* @param damage0 int
* @param defender GameEntity that's bein attacked
* @param overrideOrder override combatant order
*/
public VAssignDamage(final Card attacker0, final List<Card> defenderCards, final int damage0, final GameEntity defender, boolean overrideOrder) {
// Set damage storage vars
this.totalDamageToAssign = damage0;
this.defender = defender;
this.attackerHasDeathtouch = attacker0.hasKeyword("Deathtouch");
this.attackerHasInfect = attacker0.hasKeyword("Infect");
this.attackerHasTrample = defender != null && attacker0.hasKeyword("Trample");
this.overrideCombatantOrder = overrideOrder;
// Top-level UI stuff
/*final SkinnedPanel pnlMain = new SkinnedPanel();
pnlMain.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2));
// Attacker area
final FCardPanel pnlAttacker = new FCardPanel(attacker0);
pnlAttacker.setOpaque(false);
pnlAttacker.setCardBounds(0, 0, 105, 150);
final JPanel pnlInfo = new JPanel(new MigLayout("insets 0, gap 0, wrap"));
pnlInfo.setOpaque(false);
pnlInfo.add(lblTotalDamage, "gap 0 0 20px 5px");
pnlInfo.add(new FLabel.Builder().text("Left click: Assign 1 damage. (Left Click + Control): Assign remaining damage up to lethal").build(), "gap 0 0 0 5px");
pnlInfo.add(new FLabel.Builder().text("Right click: Unassign 1 damage. (Right Click + Control): Unassign all damage.").build(), "gap 0 0 0 5px");
// Defenders area
final JPanel pnlDefenders = new JPanel();
pnlDefenders.setOpaque(false);
int cols = attackerHasTrample ? defenderCards.size() + 1 : defenderCards.size();
final String wrap = "wrap " + Integer.toString(cols);
pnlDefenders.setLayout(new MigLayout("insets 0, gap 0, ax center, " + wrap));
final FScrollPane scrDefenders = new FScrollPane(pnlDefenders, false);
// Top row of cards...
for (final Card c : defenderCards) {
DamageTarget dt = new DamageTarget(c, new FLabel.Builder().text("0").fontSize(18).fontAlign(SwingConstants.CENTER).build());
this.damage.put(c, dt);
this.defenders.add(dt);
addPanelForDefender(pnlDefenders, c);
}
if (attackerHasTrample) {
DamageTarget dt = new DamageTarget(null, new FLabel.Builder().text("0").fontSize(18).fontAlign(SwingConstants.CENTER).build());
this.damage.put(null, dt);
this.defenders.add(dt);
Card fakeCard;
if (defender instanceof Card)
fakeCard = (Card)defender;
else if (defender instanceof Player) {
fakeCard = new Card(-1);
fakeCard.setName(this.defender.getName());
fakeCard.setOwner((Player)defender);
Player p = (Player)defender;
fakeCard.setImageKey(p.getLobbyPlayer().getIconImageKey());
} else {
fakeCard = new Card(-2);
fakeCard.setName(this.defender.getName());
}
addPanelForDefender(pnlDefenders, fakeCard);
}
// Add "opponent placeholder" card if trample allowed
// If trample allowed, make card placeholder
// ... bottom row of labels.
for (DamageTarget l : defenders) {
pnlDefenders.add(l.label, "w 145px!, h 30px!, gap 5px 5px 0 5px");
}
btnOK.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent arg0) { finish(); } });
btnReset.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent arg0) { resetAssignedDamage(); initialAssignDamage(false); } });
btnAuto.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent arg0) { resetAssignedDamage(); initialAssignDamage(true); finish(); } });
// Final UI layout
pnlMain.setLayout(new MigLayout("insets 0, gap 0, wrap 2, ax center"));
pnlMain.add(pnlAttacker, "w 125px!, h 160px!, gap 50px 0 0 15px");
pnlMain.add(pnlInfo, "gap 20px 0 0 15px");
pnlMain.add(scrDefenders, "w 96%!, gap 2% 0 0 0, pushy, growy, ax center, span 2");
pnlMain.add(lblAssignRemaining, "w 96%!, gap 2% 0 0 0, ax center, span 2");
JPanel pnlButtons = new JPanel(new MigLayout("insets 0, gap 0, ax center"));
pnlButtons.setOpaque(false);
pnlButtons.add(btnAuto, "w 110px!, h 30px!, gap 0 10px 0 0");
pnlButtons.add(btnOK, "w 110px!, h 30px!, gap 0 10px 0 0");
pnlButtons.add(btnReset, "w 110px!, h 30px!");
pnlMain.add(pnlButtons, "ax center, w 350px!, gap 10px 10px 10px 10px, span 2");
overlay.add(pnlMain);
pnlMain.getRootPane().setDefaultButton(btnOK);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
btnAuto.requestFocusInWindow();
}
});
initialAssignDamage(false);
SOverlayUtils.showOverlay();
this.dlg.setUndecorated(true);
this.dlg.setContentPane(pnlMain);
this.dlg.setSize(new Dimension(wDlg, hDlg));
this.dlg.setLocation((overlay.getWidth() - wDlg) / 2, (overlay.getHeight() - hDlg) / 2);
this.dlg.setModalityType(ModalityType.APPLICATION_MODAL);
this.dlg.setVisible(true);*/
}
/**
* TODO: Write javadoc for this method.
* @param pnlDefenders
* @param defender
*/
private void addPanelForDefender(final Card defender) {
final FCardPanel cp = new FCardPanel(defender);
cp.setBounds(0, 0, 145, 170);
}
/**
* TODO: Write javadoc for this method.
* @param source
* @param meta
* @param isLMB
*/
private void assignDamageTo(Card source, boolean meta, boolean isAdding) {
if (!damage.containsKey(source))
source = null;
// If trying to assign to the defender, follow the normal assignment rules
// No need to check for "active" creature assignee when overiding combatant order
if ((source == null || source == this.defender || !this.overrideCombatantOrder) && isAdding &&
!VAssignDamage.this.canAssignTo(source)) {
return;
}
// If lethal damage has already been assigned just act like it's 0.
int lethalDamage = getDamageToKill(source);
int damageItHad = damage.get(source).damage;
int leftToKill = Math.max(0, lethalDamage - damageItHad);
int damageToAdd = isAdding ? 1 : -1;
int leftToAssign = getRemainingDamage();
// Left click adds damage, right click substracts damage.
// Hold Ctrl to assign lethal damage, Ctrl-click again on a creature with lethal damage to assign all available damage to it
if (meta) {
if (isAdding) {
damageToAdd = leftToKill > 0 ? leftToKill : leftToAssign;
}
else {
damageToAdd = damageItHad > lethalDamage ? lethalDamage - damageItHad : -damageItHad;
}
}
if (damageToAdd > leftToAssign) {
damageToAdd = leftToAssign;
}
// cannot assign first blocker less than lethal damage except when overriding order
boolean isFirstBlocker = defenders.get(0).card == source;
if (!this.overrideCombatantOrder && isFirstBlocker && damageToAdd + damageItHad < lethalDamage) {
return;
}
if (damageToAdd == 0 || damageToAdd + damageItHad < 0) {
return;
}
addDamage(source, damageToAdd);
checkDamageQueue();
updateLabels();
}
private void checkDamageQueue() {
// Clear out any Damage that shouldn't be assigned to other combatants
boolean hasAliveEnemy = false;
for (DamageTarget dt : defenders) {
int lethal = getDamageToKill(dt.card);
int damage = dt.damage;
// If overriding combatant order, make sure everything has lethal if defender has damage assigned to it
// Otherwise, follow normal combatant order
if (hasAliveEnemy && (!this.overrideCombatantOrder || dt.card == null || dt.card == this.defender)) {
dt.damage = 0;
}
else {
hasAliveEnemy |= damage < lethal;
}
}
}
// will assign all damage to defenders and rest to player, if present
private void initialAssignDamage(boolean toAllBlockers) {
if (!toAllBlockers && this.overrideCombatantOrder) {
// Don't auto assign the first damage when overriding combatant order
updateLabels();
return;
}
int dmgLeft = totalDamageToAssign;
DamageTarget dtLast = null;
for(DamageTarget dt : defenders) { // MUST NOT RUN WITH EMPTY collection
int lethal = getDamageToKill(dt.card);
int damage = Math.min(lethal, dmgLeft);
addDamage(dt.card, damage);
dmgLeft -= damage;
dtLast = dt;
if (dmgLeft <= 0 || !toAllBlockers) { break; }
}
if (dmgLeft < 0) {
throw new RuntimeException("initialAssignDamage managed to assign more damage than it could");
}
if (toAllBlockers && dmgLeft > 0) {
// flush the remaining damage into last defender if assigning all damage
addDamage(dtLast.card, dmgLeft);
}
updateLabels();
}
/** Reset Assign Damage back to how it was at the beginning. */
private void resetAssignedDamage() {
for(DamageTarget dt : defenders)
dt.damage = 0;
}
private void addDamage(final Card card, int addedDamage) {
// If we don't have enough left or we're trying to unassign too much return
int canAssign = getRemainingDamage();
if (canAssign < addedDamage) {
addedDamage = canAssign;
}
DamageTarget dt = damage.get(card);
dt.damage = Math.max(0, addedDamage + dt.damage);
}
private int getRemainingDamage() {
int spent = 0;
for (DamageTarget dt : defenders) {
spent += dt.damage;
}
return totalDamageToAssign - spent;
}
/** Updates labels and other UI elements.
* @param index index of the last assigned damage*/
private void updateLabels() {
int damageLeft = totalDamageToAssign;
boolean allHaveLethal = true;
for (DamageTarget dt : defenders) {
int dmg = dt.damage;
damageLeft -= dmg;
int lethal = getDamageToKill(dt.card);
int overkill = dmg - lethal;
StringBuilder sb = new StringBuilder();
sb.append(dmg);
if(overkill >= 0) {
sb.append(" (Lethal");
if(overkill > 0)
sb.append(" +").append(overkill);
sb.append(")");
}
allHaveLethal &= dmg >= lethal;
dt.label.setText(sb.toString());
}
this.lblTotalDamage.setText(String.format("Available damage points: %d (of %d)", damageLeft, this.totalDamageToAssign));
btnOK.setEnabled(damageLeft == 0);
lblAssignRemaining.setVisible(allHaveLethal && damageLeft > 0);
}
// Dumps damage onto cards. Damage must be stored first, because if it is
// assigned dynamically, the cards die off and further damage to them can't
// be modified.
private void finish() {
if (getRemainingDamage() > 0) {
return;
}
//dlg.dispose();
}
private int getDamageToKill(Card source) {
int lethalDamage = 0;
if (source == null) {
if (defender instanceof Player) {
Player p = (Player)defender;
lethalDamage = attackerHasInfect ? p.getGame().getRules().getPoisonCountersToLose() - p.getPoisonCounters() : p.getLife();
}
else if (defender instanceof Card) { // planeswalker
Card pw = (Card)defender;
lethalDamage = pw.getCounters(CounterType.LOYALTY);
}
}
else {
lethalDamage = VAssignDamage.this.attackerHasDeathtouch ? 1 : Math.max(0, source.getLethalDamage());
}
return lethalDamage;
}
public Map<Card, Integer> getDamageMap() {
Map<Card, Integer> result = new HashMap<Card, Integer>();
for (DamageTarget dt : defenders) {
result.put(dt.card, dt.damage);
}
return result;
}
}

View File

@@ -1,21 +1,14 @@
package forge.screens.match.views; package forge.screens.match.views;
import java.util.ArrayList;
import java.util.List;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.zone.ZoneType;
import forge.toolbox.FCardPanel; import forge.toolbox.FCardPanel;
import forge.toolbox.FDisplayObject;
import forge.toolbox.FScrollPane;
public class VField extends FScrollPane { public class VField extends VZoneDisplay {
private boolean flipped; private boolean flipped;
private final List<FCardPanel> creatures = new ArrayList<FCardPanel>();
private final List<FCardPanel> lands = new ArrayList<FCardPanel>();
private final List<FCardPanel> otherPermanents = new ArrayList<FCardPanel>();
public VField() { public VField() {
super(ZoneType.Battlefield);
} }
public boolean isFlipped() { public boolean isFlipped() {
@@ -35,29 +28,29 @@ public class VField extends FScrollPane {
@Override @Override
protected void doLayout(float width, float height) { protected void doLayout(float width, float height) {
float x = 0; float x, y;
float y = 0; float x1 = 0;
float x2 = 0;
float y1 = 0;
float cardSize = height / 2; float cardSize = height / 2;
float y2 = cardSize;
for (FCardPanel cardPanel : creatures) { if (flipped) {
cardPanel.setBounds(x, y, cardSize, cardSize); y1 = y2;
x += cardSize; y2 = 0;
}
x = 0;
y += cardSize;
for (FCardPanel cardPanel : lands) {
cardPanel.setBounds(x, y, cardSize, cardSize);
x += cardSize;
}
for (FCardPanel cardPanel : otherPermanents) {
cardPanel.setBounds(x, y, cardSize, cardSize);
x += cardSize;
} }
if (flipped) { //flip all positions across x-axis if needed for (FCardPanel cardPanel : cardPanels) {
for (FDisplayObject child : getChildren()) { if (cardPanel.getCard().isCreature()) {
child.setTop(height - child.getBottom()); x = x1;
y = y1;
x1 += cardSize;
} }
else {
x = x2;
y = y2;
x2 += cardSize;
}
cardPanel.setBounds(x, y, cardSize, cardSize);
} }
} }
} }

View File

@@ -4,14 +4,11 @@ import org.apache.commons.lang3.StringUtils;
import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment; import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;
import forge.FThreads;
import forge.Forge.Graphics; import forge.Forge.Graphics;
import forge.assets.FSkinColor; import forge.assets.FSkinColor;
import forge.assets.FSkinFont; import forge.assets.FSkinFont;
import forge.assets.FSkinColor.Colors; import forge.assets.FSkinColor.Colors;
import forge.game.Game; import forge.game.Game;
import forge.game.GameRules;
import forge.game.Match;
import forge.screens.match.input.InputProxy; import forge.screens.match.input.InputProxy;
import forge.toolbox.FButton; import forge.toolbox.FButton;
import forge.toolbox.FContainer; import forge.toolbox.FContainer;

View File

@@ -9,7 +9,7 @@ import forge.toolbox.FScrollPane;
public class VZoneDisplay extends FScrollPane { public class VZoneDisplay extends FScrollPane {
private ZoneType zoneType; private ZoneType zoneType;
private final List<FCardPanel> cards = new ArrayList<FCardPanel>(); protected final List<FCardPanel> cardPanels = new ArrayList<FCardPanel>();
public VZoneDisplay(ZoneType zoneType0) { public VZoneDisplay(ZoneType zoneType0) {
zoneType = zoneType0; zoneType = zoneType0;
@@ -19,8 +19,12 @@ public class VZoneDisplay extends FScrollPane {
return zoneType; return zoneType;
} }
public Iterable<FCardPanel> getCardPanels() {
return cardPanels;
}
public int getCount() { public int getCount() {
return 99; //TODO return cardPanels.size();
} }
public void update() { public void update() {
@@ -34,7 +38,7 @@ public class VZoneDisplay extends FScrollPane {
float cardHeight = height; float cardHeight = height;
float cardWidth = ((cardHeight - 2 * FCardPanel.PADDING) / FCardPanel.ASPECT_RATIO) + 2 * FCardPanel.PADDING; //ensure aspect ratio maintained after padding applied float cardWidth = ((cardHeight - 2 * FCardPanel.PADDING) / FCardPanel.ASPECT_RATIO) + 2 * FCardPanel.PADDING; //ensure aspect ratio maintained after padding applied
for (FCardPanel cardPanel : cards) { for (FCardPanel cardPanel : cardPanels) {
cardPanel.setBounds(x, y, cardWidth, cardHeight); cardPanel.setBounds(x, y, cardWidth, cardHeight);
x += cardWidth; x += cardWidth;
} }

View File

@@ -6,14 +6,26 @@ import forge.game.card.Card;
public class FCardPanel extends FDisplayObject { public class FCardPanel extends FDisplayObject {
public static final float ASPECT_RATIO = 3.5f / 2.5f; public static final float ASPECT_RATIO = 3.5f / 2.5f;
public static final float PADDING = 3; //scale to leave vertical space between public static final float PADDING = 2; //scale to leave vertical space between
private final Card card; private final Card card;
private boolean highlighted;
public FCardPanel(Card card0) { public FCardPanel(Card card0) {
card = card0; card = card0;
} }
public Card getCard() {
return card;
}
public boolean isHighlighted() {
return highlighted;
}
public void setHighlighted(boolean highlighted0) {
highlighted = highlighted0;
}
@Override @Override
public void draw(Graphics g) { public void draw(Graphics g) {
float x = PADDING; float x = PADDING;

View File

@@ -0,0 +1,485 @@
/*
* 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.utils;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import forge.card.CardCharacteristicName;
import forge.game.Game;
import forge.game.GameType;
import forge.game.PlanarDice;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterType;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.item.IPaperCard;
import forge.item.PaperCard;
import forge.model.FModel;
import forge.player.HumanPlay;
import forge.screens.match.FControl;
import forge.screens.match.input.InputSelectCardsFromList;
import forge.toolbox.FOptionPane;
import forge.toolbox.GuiChoose;
import forge.toolbox.GuiDialog;
import forge.utils.ForgePreferences.FPref;
import javax.swing.*;
import java.io.*;
import java.util.*;
import java.util.Map.Entry;
public final class GuiDisplayUtil {
private GuiDisplayUtil() {
throw new AssertionError();
}
public static void devModeGenerateMana() {
final Card dummy = new Card(-777777);
dummy.setOwner(getGame().getPhaseHandler().getPriorityPlayer());
Map<String, String> produced = new HashMap<String, String>();
produced.put("Produced", "W W W W W W W U U U U U U U B B B B B B B G G G G G G G R R R R R R R 7");
final AbilityManaPart abMana = new AbilityManaPart(dummy, produced);
getGame().getAction().invoke(new Runnable() {
@Override public void run() { abMana.produceMana(null); }
});
}
public static void devSetupGameState() {
int humanLife = -1;
int computerLife = -1;
final Map<ZoneType, String> humanCardTexts = new EnumMap<ZoneType, String>(ZoneType.class);
final Map<ZoneType, String> aiCardTexts = new EnumMap<ZoneType, String>(ZoneType.class);
String tChangePlayer = "NONE";
String tChangePhase = "NONE";
final String wd = ".";
final JFileChooser fc = new JFileChooser(wd);
final int rc = fc.showDialog(null, "Select Game State File");
if (rc != JFileChooser.APPROVE_OPTION) {
return;
}
try {
final FileInputStream fstream = new FileInputStream(fc.getSelectedFile().getAbsolutePath());
final DataInputStream in = new DataInputStream(fstream);
final BufferedReader br = new BufferedReader(new InputStreamReader(in));
String temp = "";
while ((temp = br.readLine()) != null) {
final String[] tempData = temp.split("=");
if (tempData.length < 2 || temp.charAt(0) == '#') {
continue;
}
final String categoryName = tempData[0].toLowerCase();
final String categoryValue = tempData[1];
if (categoryName.equals("humanlife")) humanLife = Integer.parseInt(categoryValue);
else if (categoryName.equals("ailife")) computerLife = Integer.parseInt(categoryValue);
else if (categoryName.equals("activeplayer")) tChangePlayer = categoryValue.trim().toLowerCase();
else if (categoryName.equals("activephase")) tChangePhase = categoryValue;
else if (categoryName.equals("humancardsinplay")) humanCardTexts.put(ZoneType.Battlefield, categoryValue);
else if (categoryName.equals("aicardsinplay")) aiCardTexts.put(ZoneType.Battlefield, categoryValue);
else if (categoryName.equals("humancardsinhand")) humanCardTexts.put(ZoneType.Hand, categoryValue);
else if (categoryName.equals("aicardsinhand")) aiCardTexts.put(ZoneType.Hand, categoryValue);
else if (categoryName.equals("humancardsingraveyard")) humanCardTexts.put(ZoneType.Graveyard, categoryValue);
else if (categoryName.equals("aicardsingraveyard")) aiCardTexts.put(ZoneType.Graveyard, categoryValue);
else if (categoryName.equals("humancardsinlibrary")) humanCardTexts.put(ZoneType.Library, categoryValue);
else if (categoryName.equals("aicardsinlibrary")) aiCardTexts.put(ZoneType.Library, categoryValue);
else if (categoryName.equals("humancardsinexile")) humanCardTexts.put(ZoneType.Exile, categoryValue);
else if (categoryName.equals("aicardsinexile")) aiCardTexts.put(ZoneType.Exile, categoryValue);
}
in.close();
}
catch (final FileNotFoundException fnfe) {
FOptionPane.showErrorDialog("File not found: " + fc.getSelectedFile().getAbsolutePath());
}
catch (final Exception e) {
FOptionPane.showErrorDialog("Error loading battle setup file!");
return;
}
setupGameState(humanLife, computerLife, humanCardTexts, aiCardTexts, tChangePlayer, tChangePhase);
}
private static void setupGameState(final int humanLife, final int computerLife, final Map<ZoneType, String> humanCardTexts,
final Map<ZoneType, String> aiCardTexts, final String tChangePlayer, final String tChangePhase) {
final Game game = getGame();
game.getAction().invoke(new Runnable() {
@Override
public void run() {
final Player human = game.getPlayers().get(0);
final Player ai = game.getPlayers().get(1);
Player newPlayerTurn = tChangePlayer.equals("human") ? newPlayerTurn = human : tChangePlayer.equals("ai") ? newPlayerTurn = ai : null;
PhaseType newPhase = tChangePhase.trim().equalsIgnoreCase("none") ? null : PhaseType.smartValueOf(tChangePhase);
game.getPhaseHandler().devModeSet(newPhase, newPlayerTurn);
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
devSetupPlayerState(humanLife, humanCardTexts, human);
devSetupPlayerState(computerLife, aiCardTexts, ai);
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
game.getAction().checkStaticAbilities();
}
});
}
private static void devSetupPlayerState(int life, Map<ZoneType, String> cardTexts, final Player p) {
Map<ZoneType, List<Card>> humanCards = new EnumMap<ZoneType, List<Card>>(ZoneType.class);
for(Entry<ZoneType, String> kv : cardTexts.entrySet()) {
humanCards.put(kv.getKey(), GuiDisplayUtil.devProcessCardsForZone(kv.getValue().split(";"), p));
}
if (life > 0) p.setLife(life, null);
for (Entry<ZoneType, List<Card>> kv : humanCards.entrySet()) {
if (kv.getKey() == ZoneType.Battlefield) {
for (final Card c : kv.getValue()) {
p.getZone(ZoneType.Hand).add(c);
p.getGame().getAction().moveToPlay(c);
c.setSickness(false);
}
} else {
p.getZone(kv.getKey()).setCards(kv.getValue());
}
}
}
/**
* <p>
* devProcessCardsForZone.
* </p>
*
* @param data
* an array of {@link java.lang.String} objects.
* @param player
* a {@link forge.game.player.Player} object.
* @return a {@link forge.CardList} object.
*/
private static List<Card> devProcessCardsForZone(final String[] data, final Player player) {
final List<Card> cl = new ArrayList<Card>();
for (final String element : data) {
final String[] cardinfo = element.trim().split("\\|");
final Card c = Card.fromPaperCard(FModel.getMagicDb().getCommonCards().getCard(cardinfo[0]), player);
boolean hasSetCurSet = false;
for (final String info : cardinfo) {
if (info.startsWith("Set:")) {
c.setCurSetCode(info.substring(info.indexOf(':') + 1));
hasSetCurSet = true;
} else if (info.equalsIgnoreCase("Tapped:True")) {
c.tap();
} else if (info.startsWith("Counters:")) {
final String[] counterStrings = info.substring(info.indexOf(':') + 1).split(",");
for (final String counter : counterStrings) {
c.addCounter(CounterType.valueOf(counter), 1, true);
}
} else if (info.equalsIgnoreCase("SummonSick:True")) {
c.setSickness(true);
} else if (info.equalsIgnoreCase("FaceDown:True")) {
c.setState(CardCharacteristicName.FaceDown);
}
}
if (!hasSetCurSet) {
c.setCurSetCode(c.getMostRecentSet());
}
cl.add(c);
}
return cl;
}
/**
* <p>
* devModeTutor.
* </p>
*
* @since 1.0.15
*/
public static void devModeTutor() {
Player pPriority = getGame().getPhaseHandler().getPriorityPlayer();
if (pPriority == null) {
GuiDialog.message("No player has priority now, can't tutor from their deck at the moment");
return;
}
final List<Card> lib = pPriority.getCardsIn(ZoneType.Library);
final Card card = GuiChoose.oneOrNone("Choose a card", lib);
if (card == null) { return; }
getGame().getAction().invoke(new Runnable() {
@Override
public void run() {
getGame().getAction().moveToHand(card);
}
});
}
/**
* <p>
* devModeAddCounter.
* </p>
*
* @since 1.0.15
*/
public static void devModeAddCounter() {
final Card card = GuiChoose.oneOrNone("Add counters to which card?", getGame().getCardsIn(ZoneType.Battlefield));
if (card == null) { return; }
final CounterType counter = GuiChoose.oneOrNone("Which type of counter?", CounterType.values());
if (counter == null) { return; }
final Integer count = GuiChoose.getInteger("How many counters?", 1, Integer.MAX_VALUE, 10);
if (count == null) { return; }
card.addCounter(counter, count, false);
}
/**
* <p>
* devModeTapPerm.
* </p>
*
* @since 1.0.15
*/
public static void devModeTapPerm() {
final Game game = getGame();
game.getAction().invoke(new Runnable() {
@Override
public void run() {
final List<Card> untapped = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Predicates.not(CardPredicates.Presets.TAPPED));
InputSelectCardsFromList inp = new InputSelectCardsFromList(0, Integer.MAX_VALUE, untapped);
inp.setCancelAllowed(true);
inp.setMessage("Choose permanents to tap");
inp.showAndWait();
if (!inp.hasCancelled()) {
for (Card c : inp.getSelected()) {
c.tap();
}
}
}
});
}
/**
* <p>
* devModeUntapPerm.
* </p>
*
* @since 1.0.15
*/
public static void devModeUntapPerm() {
final Game game = getGame();
game.getAction().invoke(new Runnable() {
@Override
public void run() {
final List<Card> tapped = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.TAPPED);
InputSelectCardsFromList inp = new InputSelectCardsFromList(0, Integer.MAX_VALUE, tapped);
inp.setCancelAllowed(true);
inp.setMessage("Choose permanents to untap");
inp.showAndWait();
if( !inp.hasCancelled() )
for(Card c : inp.getSelected())
c.untap();
}
});
}
/**
* <p>
* devModeSetLife.
* </p>
*
* @since 1.1.3
*/
public static void devModeSetLife() {
final List<Player> players = getGame().getPlayers();
final Player player = GuiChoose.oneOrNone("Set life for which player?", players);
if (player == null) { return; }
final Integer life = GuiChoose.getInteger("Set life to what?", 0);
if (life == null) { return; }
player.setLife(life, null);
}
/**
* <p>
* devModeTutorAnyCard.
* </p>
*
* @since 1.2.7
*/
public static void devModeCardToHand() {
final List<Player> players = getGame().getPlayers();
final Player p = GuiChoose.oneOrNone("Put card in hand for which player?", players);
if (null == p) {
return;
}
final List<PaperCard> cards = Lists.newArrayList(FModel.getMagicDb().getCommonCards().getUniqueCards());
Collections.sort(cards);
// use standard forge's list selection dialog
final IPaperCard c = GuiChoose.oneOrNone("Name the card", cards);
if (c == null) {
return;
}
getGame().getAction().invoke(new Runnable() { @Override public void run() {
getGame().getAction().moveToHand(Card.fromPaperCard(c, p));
}});
}
public static void devModeCardToBattlefield() {
final List<Player> players = getGame().getPlayers();
final Player p = GuiChoose.oneOrNone("Put card in play for which player?", players);
if (null == p) {
return;
}
final List<PaperCard> cards = Lists.newArrayList(FModel.getMagicDb().getCommonCards().getUniqueCards());
Collections.sort(cards);
// use standard forge's list selection dialog
final IPaperCard c = GuiChoose.oneOrNone("Name the card", cards);
if (c == null) {
return;
}
final Game game = getGame();
game.getAction().invoke(new Runnable() {
@Override public void run() {
final Card forgeCard = Card.fromPaperCard(c, p);
if (c.getRules().getType().isLand()) {
game.getAction().moveToPlay(forgeCard);
} else {
final List<SpellAbility> choices = forgeCard.getBasicSpells();
if (choices.isEmpty()) {
return; // when would it happen?
}
final SpellAbility sa = choices.size() == 1 ? choices.get(0) : GuiChoose.oneOrNone("Choose", choices);
if (sa == null) {
return; // happens if cancelled
}
game.getAction().moveToHand(forgeCard); // this is really needed (for rollbacks at least)
// Human player is choosing targets for an ability controlled by chosen player.
sa.setActivatingPlayer(p);
HumanPlay.playSaWithoutPayingManaCost(game, sa, true);
}
game.getStack().addAllTirggeredAbilitiesToStack(); // playSa could fire some triggers
}
});
}
public static void devModeRiggedPlanarRoll() {
final List<Player> players = getGame().getPlayers();
final Player player = GuiChoose.oneOrNone("Which player should roll?", players);
if (player == null) { return; }
final PlanarDice res = GuiChoose.oneOrNone("Choose result", PlanarDice.values());
if (res == null) { return; }
System.out.println("Rigging planar dice roll: " + res.toString());
//DBG
//System.out.println("ActivePlanes: " + getGame().getActivePlanes());
//System.out.println("CommandPlanes: " + getGame().getCardsIn(ZoneType.Command));
getGame().getAction().invoke(new Runnable() {
@Override
public void run() {
PlanarDice.roll(player, res);
}
});
}
public static void devModePlaneswalkTo() {
final Game game = getGame();
if (!game.getRules().hasAppliedVariant(GameType.Planechase)) { return; }
final Player p = game.getPhaseHandler().getPlayerTurn();
final List<PaperCard> allPlanars = new ArrayList<PaperCard>();
for (PaperCard c : FModel.getMagicDb().getVariantCards().getAllCards()) {
if (c.getRules().getType().isPlane() || c.getRules().getType().isPhenomenon()) {
allPlanars.add(c);
}
}
Collections.sort(allPlanars);
// use standard forge's list selection dialog
final IPaperCard c = GuiChoose.oneOrNone("Name the card", allPlanars);
if (c == null) { return; }
final Card forgeCard = Card.fromPaperCard(c, p);
forgeCard.setOwner(p);
getGame().getAction().invoke(new Runnable() {
@Override
public void run() {
getGame().getAction().changeZone(null, p.getZone(ZoneType.PlanarDeck), forgeCard, 0);
PlanarDice.roll(p, PlanarDice.Planeswalk);
}
});
}
private static Game getGame() {
return FControl.getGame();
}
public static String getPlayerName() {
return FModel.getPreferences().getPref(FPref.PLAYER_NAME);
}
public static String personalizeHuman(String text) {
String playerName = FModel.getPreferences().getPref(FPref.PLAYER_NAME);
return text.replaceAll("(?i)human", playerName);
}
} // end class GuiDisplayUtil

View File

@@ -0,0 +1,139 @@
/*
* 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.utils;
import forge.game.card.Card;
import forge.screens.match.FControl;
import forge.screens.match.views.VPlayerPanel;
import forge.toolbox.FCardPanel;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
/**
* <p>
* GuiUtils class.
* </p>
*
* @author Forge
* @version $Id: GuiUtils.java 24769 2014-02-09 13:56:04Z Hellfish $
*/
public final class GuiUtils {
private GuiUtils() {
throw new AssertionError();
}
/**
* Attempts to create a font from a filename. Concise error reported if
* exceptions found.
*
* @param filename
* String
* @return Font
*/
public static Font newFont(final String filename) {
final File file = new File(filename);
Font ttf = null;
try {
ttf = Font.createFont(Font.TRUETYPE_FONT, file);
} catch (final FontFormatException e) {
System.err.println("GuiUtils > newFont: bad font format \"" + filename + "\"");
} catch (final IOException e) {
System.err.println("GuiUtils > newFont: can't find \"" + filename + "\"");
}
return ttf;
}
private static final int minItemWidth = 100;
private static final int itemHeight = 25;
public static void setMenuItemSize(JMenuItem item) {
item.setPreferredSize(new Dimension(Math.max(item.getPreferredSize().width, minItemWidth), itemHeight));
}
public static JMenu createMenu(String label) {
if (label.startsWith("<html>")) { //adjust label if HTML
label = "<html>" + "<div style='height: " + itemHeight + "px; margin-top: 6px;'>" + label.substring(6, label.length() - 7) + "</div></html>";
}
JMenu menu = new JMenu(label);
setMenuItemSize(menu);
return menu;
}
public static JMenuItem createMenuItem(String label, KeyStroke accelerator, final Runnable onClick, boolean enabled, boolean bold) {
if (label.startsWith("<html>")) { //adjust label if HTML
label = "<html>" + "<div style='height: " + itemHeight + "px; margin-top: 6px;'>" + label.substring(6, label.length() - 7) + "</div></html>";
}
JMenuItem item = new JMenuItem(label);
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (null != onClick) {
onClick.run();
}
}
});
item.setEnabled(enabled);
item.setAccelerator(accelerator);
if (bold) {
item.setFont(item.getFont().deriveFont(Font.BOLD));
}
setMenuItemSize(item);
return item;
}
public static void addMenuItem(JPopupMenu parent, String label, KeyStroke accelerator, Runnable onClick) {
parent.add(createMenuItem(label, accelerator, onClick, true, false));
}
public static void addMenuItem(JMenuItem parent, String label, KeyStroke accelerator, Runnable onClick) {
parent.add(createMenuItem(label, accelerator, onClick, true, false));
}
public static void addMenuItem(JPopupMenu parent, String label, KeyStroke accelerator, Runnable onClick, boolean enabled) {
parent.add(createMenuItem(label, accelerator, onClick, enabled, false));
}
public static void addMenuItem(JMenuItem parent, String label, KeyStroke accelerator, Runnable onClick, boolean enabled) {
parent.add(createMenuItem(label, accelerator, onClick, enabled, false));
}
public static void addMenuItem(JPopupMenu parent, String label, KeyStroke accelerator, Runnable onClick, boolean enabled, boolean bold) {
parent.add(createMenuItem(label, accelerator, onClick, enabled, bold));
}
public static void addMenuItem(JMenuItem parent, String label, KeyStroke accelerator, Runnable onClick, boolean enabled, boolean bold) {
parent.add(createMenuItem(label, accelerator, onClick, enabled, bold));
}
public static void addSeparator(JPopupMenu parent) {
parent.add(new JSeparator());
}
public static void addSeparator(JMenuItem parent) {
parent.add(new JSeparator());
}
}