mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
Flesh out PlayerControllerHuman
This commit is contained in:
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -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
387
forge-m-base/src/forge/player/TargetSelection.java
Normal file
387
forge-m-base/src/forge/player/TargetSelection.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
|||||||
438
forge-m-base/src/forge/screens/match/views/VAssignDamage.java
Normal file
438
forge-m-base/src/forge/screens/match/views/VAssignDamage.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
485
forge-m-base/src/forge/utils/GuiDisplayUtil.java
Normal file
485
forge-m-base/src/forge/utils/GuiDisplayUtil.java
Normal 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
|
||||||
139
forge-m-base/src/forge/utils/GuiUtils.java
Normal file
139
forge-m-base/src/forge/utils/GuiUtils.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user