Implement missing cards with dividable shields for damage replacement effects

This commit is contained in:
Lyu Zong-Hong
2021-06-05 18:18:34 +09:00
parent f95fb7d08b
commit ab31c8fa9d
25 changed files with 1077 additions and 173 deletions

View File

@@ -81,9 +81,9 @@ import forge.util.collect.FCollection;
import forge.util.collect.FCollectionView;
/**
/**
* A prototype for player controller class
*
*
* Handles phase skips for now.
*/
public class PlayerControllerAi extends PlayerController {
@@ -94,11 +94,11 @@ public class PlayerControllerAi extends PlayerController {
brains = new AiController(p, game);
}
public void allowCheatShuffle(boolean value){
brains.allowCheatShuffle(value);
}
public void setUseSimulation(boolean value) {
brains.setUseSimulation(value);
}
@@ -131,6 +131,12 @@ public class PlayerControllerAi extends PlayerController {
return ComputerUtilCombat.distributeAIDamage(attacker, blockers, damageDealt, defender, overrideOrder);
}
@Override
public Map<GameEntity, Integer> divideShield(Card effectSource, Map<GameEntity, Integer> affected, int shieldAmount) {
// TODO: AI current can't use this so this is not implemented.
return new HashMap<>();
}
@Override
public Integer announceRequirements(SpellAbility ability, String announce) {
// For now, these "announcements" are made within the AI classes of the appropriate SA effects
@@ -157,7 +163,7 @@ public class PlayerControllerAi extends PlayerController {
case BidLife:
return 0;
default:
return null;
return null;
}
}
return null; // return incorrect value to indicate that
@@ -240,7 +246,7 @@ public class PlayerControllerAi extends PlayerController {
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) {
return getAi().confirmAction(sa, mode, message);
}
@Override
public boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode mode, String string,
int bid, Player winner) {
@@ -646,7 +652,7 @@ public class PlayerControllerAi extends PlayerController {
public List<SpellAbility> chooseSpellAbilityToPlay() {
return brains.chooseSpellAbilityToPlay();
}
@Override
public boolean playChosenSpellAbility(SpellAbility sa) {
if (sa instanceof LandAbility) {
@@ -658,7 +664,7 @@ public class PlayerControllerAi extends PlayerController {
ComputerUtil.handlePlayingSpellAbility(player, sa, getGame());
}
return true;
}
}
@Override
public CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard) {
@@ -711,7 +717,7 @@ public class PlayerControllerAi extends PlayerController {
public int chooseNumber(SpellAbility sa, String title, int min, int max) {
return brains.chooseNumber(sa, title, min, max);
}
@Override
public int chooseNumber(SpellAbility sa, String string, int min, int max, Map<String, Object> params) {
ApiType api = sa.getApi();
@@ -814,7 +820,7 @@ public class PlayerControllerAi extends PlayerController {
/*
* (non-Javadoc)
*
*
* @see
* forge.game.player.PlayerController#chooseBinary(forge.game.spellability.
* SpellAbility, java.lang.String,
@@ -855,7 +861,7 @@ public class PlayerControllerAi extends PlayerController {
}
return sa.getChosenList();
}
@Override
public byte chooseColorAllowColorless(String message, Card card, ColorSet colors) {
final String c = ComputerUtilCard.getMostProminentColor(player.getCardsIn(ZoneType.Hand));
@@ -901,7 +907,7 @@ public class PlayerControllerAi extends PlayerController {
/*
* (non-Javadoc)
*
*
* @see forge.game.player.PlayerController#chooseCounterType(java.util.List,
* forge.game.spellability.SpellAbility, java.lang.String, java.util.Map)
*/
@@ -917,7 +923,7 @@ public class PlayerControllerAi extends PlayerController {
@Override
public boolean confirmPayment(CostPart costPart, String prompt, SpellAbility sa) {
return brains.confirmPayment(costPart); // AI is expected to know what it is paying for at the moment (otherwise add another parameter to this method)
return brains.confirmPayment(costPart); // AI is expected to know what it is paying for at the moment (otherwise add another parameter to this method)
}
@Override
@@ -1105,7 +1111,7 @@ public class PlayerControllerAi extends PlayerController {
public void revealAnte(String message, Multimap<Player, PaperCard> removedAnteCards) {
// Ai won't understand that anyway
}
@Override
public Collection<? extends PaperCard> complainCardsCantPlayWell(Deck myDeck) {
return brains.complainCardsCantPlayWell(myDeck);
@@ -1153,7 +1159,7 @@ public class PlayerControllerAi extends PlayerController {
return new HashMap<>();
}
}
//Do not convoke potential blockers until after opponent's attack
final CardCollectionView blockers = ComputerUtilCard.getLikelyBlockers(ai, null);
if ((ph.isPlayerTurn(ai) && ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN)) ||

View File

@@ -57,6 +57,7 @@ public enum AbilityKey {
DefendingPlayer("DefendingPlayer"),
Destination("Destination"),
Devoured("Devoured"),
DividedShieldAmount("DividedShieldAmount"),
EchoPaid("EchoPaid"),
EffectOnly("EffectOnly"),
Exploited("Exploited"),

View File

@@ -39,6 +39,10 @@ public class ReplaceDamageEffect extends SpellAbilityEffect {
if (prevent > 0) {
int n = Math.min(dmg, prevent);
// if the effect has divided shield, use that
if (originalParams.get(AbilityKey.DividedShieldAmount) != null) {
n = Math.min(n, (Integer)originalParams.get(AbilityKey.DividedShieldAmount));
}
dmg -= n;
prevent -= n;

View File

@@ -38,6 +38,10 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect {
if (prevent > 0 && list.size() > 0 && list.get(0) instanceof GameEntity) {
int n = Math.min(dmg, prevent);
// if the effect has divided shield, use that
if (originalParams.get(AbilityKey.DividedShieldAmount) != null) {
n = Math.min(n, (Integer)originalParams.get(AbilityKey.DividedShieldAmount));
}
dmg -= n;
prevent -= n;

View File

@@ -108,6 +108,7 @@ public abstract class PlayerController {
public abstract List<PaperCard> chooseCardsYouWonToAddToDeck(List<PaperCard> losses);
public abstract Map<Card, Integer> assignCombatDamage(Card attacker, CardCollectionView blockers, int damageDealt, GameEntity defender, boolean overrideOrder);
public abstract Map<GameEntity, Integer> divideShield(Card effectSource, Map<GameEntity, Integer> affected, int shieldAmount);
public abstract Integer announceRequirements(SpellAbility ability, String announce);
public abstract CardCollectionView choosePermanentsToSacrifice(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);

View File

@@ -688,6 +688,7 @@ public class ReplacementHandler {
ApiType apiType = null;
SpellAbility bufferedSA = effectSA;
boolean needRestoreSubSA = false;
boolean needDivideShield = false;
boolean needChooseSource = false;
int shieldAmount = 0;
if (effectSA != null) {
@@ -701,7 +702,8 @@ public class ReplacementHandler {
}
}
// Determine if the prevent next N damage shield is large enough to replace all damage
// Determine if need to divide shield among affected entity and
// determine if the prevent next N damage shield is large enough to replace all damage
Map<String, String> mapParams = chosenRE.getMapParams();
if ((mapParams.containsKey("PreventionEffect") && mapParams.get("PreventionEffect").equals("NextN"))
|| apiType == ApiType.ReplaceSplitDamage) {
@@ -711,17 +713,50 @@ public class ReplacementHandler {
shieldAmount = AbilityUtils.calculateAmount(effectSA.getHostCard(), effectSA.getParamOrDefault("VarName", "1"), effectSA);
}
int damageAmount = 0;
boolean hasMultipleSource = false;
boolean hasMultipleTarget = false;
Card firstSource = null;
GameEntity firstTarget = null;
for (Map<AbilityKey, Object> runParams : runParamList) {
// Only count damage that can be prevented
if (apiType == ApiType.ReplaceDamage && Boolean.TRUE.equals(runParams.get(AbilityKey.NoPreventDamage))) continue;
damageAmount += (int) runParams.get(AbilityKey.DamageAmount);
if (firstSource == null) {
firstSource = (Card) runParams.get(AbilityKey.DamageSource);
} else if (!firstSource.equals(runParams.get(AbilityKey.DamageSource))) {
hasMultipleSource = true;
}
if (firstTarget == null) {
firstTarget = (GameEntity) runParams.get(AbilityKey.Affected);
} else if (!firstTarget.equals(runParams.get(AbilityKey.Affected))) {
hasMultipleTarget = true;
}
}
if (damageAmount > shieldAmount) {
needChooseSource = true;
if (damageAmount > shieldAmount && runParamList.size() > 1) {
if (hasMultipleSource)
needChooseSource = true;
if (effectSA.hasParam("DivideShield") && hasMultipleTarget)
needDivideShield = true;
}
}
}
// Ask the decider to divide shield among affected damage target
Map<GameEntity, Integer> shieldMap = null;
if (needDivideShield) {
Map<GameEntity, Integer> affected = new HashMap<>();
for (Map<AbilityKey, Object> runParams : runParamList) {
GameEntity target = (GameEntity) runParams.get(AbilityKey.Affected);
Integer damage = (Integer) runParams.get(AbilityKey.DamageAmount);
if (!affected.containsKey(target)) {
affected.put(target, damage);
} else {
affected.put(target, damage + affected.get(target));
}
}
shieldMap = decider.getController().divideShield(chosenRE.getHostCard(), affected, shieldAmount);
}
// CR 615.7
// If damage would be dealt to the shielded permanent or player by two or more applicable sources at the same time,
// the player or the controller of the permanent chooses which damage the shield prevents.
@@ -735,23 +770,42 @@ public class ReplacementHandler {
while (shieldAmount > 0 && !sourcesToChooseFrom.isEmpty()) {
Card source = decider.getController().chooseSingleEntityForEffect(sourcesToChooseFrom, effectSA, choiceTitle, null);
sourcesToChooseFrom.remove(source);
Map<AbilityKey, Object> runParams = null;
for (Map<AbilityKey, Object> rp : runParamList) {
if (source.equals(rp.get(AbilityKey.DamageSource))) {
runParams = rp;
break;
Iterator<Map<AbilityKey, Object>> itr = runParamList.iterator();
while (itr.hasNext()) {
Map<AbilityKey, Object> runParams = itr.next();
if (source.equals(runParams.get(AbilityKey.DamageSource))) {
itr.remove();
if (shieldMap != null) {
GameEntity target = (GameEntity) runParams.get(AbilityKey.Affected);
if (shieldMap.containsKey(target) && shieldMap.get(target) > 0) {
Integer dividedShieldAmount = shieldMap.get(target);
runParams.put(AbilityKey.DividedShieldAmount, dividedShieldAmount);
shieldAmount -= (int) dividedShieldAmount;
} else {
continue;
}
} else {
shieldAmount -= (int) runParams.get(AbilityKey.DamageAmount);
}
if (!runParams.containsKey(AbilityKey.ReplacementResultMap)) {
Map<ReplacementEffect, ReplacementResult> resultMap = new HashMap<>();
runParams.put(AbilityKey.ReplacementResultMap, resultMap);
}
runSingleReplaceDamageEffect(chosenRE, runParams, replaceCandidateMap, executedDamageMap, decider, damageMap, preventMap);
}
}
runParamList.remove(runParams);
shieldAmount -= (int) runParams.get(AbilityKey.DamageAmount);
if (!runParams.containsKey(AbilityKey.ReplacementResultMap)) {
Map<ReplacementEffect, ReplacementResult> resultMap = new HashMap<>();
runParams.put(AbilityKey.ReplacementResultMap, resultMap);
}
runSingleReplaceDamageEffect(chosenRE, runParams, replaceCandidateMap, executedDamageMap, decider, damageMap, preventMap);
}
} else {
for (Map<AbilityKey, Object> runParams : runParamList) {
if (shieldMap != null) {
GameEntity target = (GameEntity) runParams.get(AbilityKey.Affected);
if (shieldMap.containsKey(target) && shieldMap.get(target) > 0) {
Integer dividedShieldAmount = shieldMap.get(target);
runParams.put(AbilityKey.DividedShieldAmount, dividedShieldAmount);
} else {
continue;
}
}
if (!runParams.containsKey(AbilityKey.ReplacementResultMap)) {
Map<ReplacementEffect, ReplacementResult> resultMap = new HashMap<>();
runParams.put(AbilityKey.ReplacementResultMap, resultMap);

View File

@@ -760,7 +760,7 @@ public final class CMatchUI
}
}
};
if (FThreads.isGuiThread()) { // run this now whether in EDT or not so that it doesn't clobber later stuff
FThreads.invokeInEdtNowOrLater(focusRoutine);
} else {
@@ -1028,6 +1028,24 @@ public final class CMatchUI
return result.get();
}
@Override
public Map<GameEntityView, Integer> assignGenericAmount(final CardView effectSource, final Map<GameEntityView, Integer> target,
final int amount, final boolean atLeastOne, final String amountLabel) {
if (amount <= 0) {
return Collections.emptyMap();
}
final AtomicReference<Map<GameEntityView, Integer>> result = new AtomicReference<>();
FThreads.invokeInEdtAndWait(new Runnable() {
@Override
public void run() {
final VAssignGenericAmount v = new VAssignGenericAmount(CMatchUI.this, effectSource, target, amount, atLeastOne, amountLabel);
result.set(v.getAssignedMap());
}});
return result.get();
}
@Override
public void openView(final TrackableCollection<PlayerView> myPlayers) {
final GameView gameView = getGameView();
@@ -1273,28 +1291,28 @@ public final class CMatchUI
String stackNotificationPolicy = FModel.getPreferences().getPref(FPref.UI_STACK_EFFECT_NOTIFICATION_POLICY);
boolean isAi = sa.getActivatingPlayer().isAI();
boolean isTrigger = sa.isTrigger();
int stackIndex = event.stackIndex;
if(stackIndex == nextNotifiableStackIndex) {
int stackIndex = event.stackIndex;
if(stackIndex == nextNotifiableStackIndex) {
if(ForgeConstants.STACK_EFFECT_NOTIFICATION_ALWAYS.equals(stackNotificationPolicy) || (ForgeConstants.STACK_EFFECT_NOTIFICATION_AI_AND_TRIGGERED.equals(stackNotificationPolicy) && (isAi || isTrigger))) {
// We can go and show the modal
SpellAbilityStackInstance si = event.si;
MigLayout migLayout = new MigLayout("insets 15, left, gap 30, fill");
JPanel mainPanel = new JPanel(migLayout);
final Dimension parentSize = JOptionPane.getRootFrame().getSize();
Dimension maxSize = new Dimension(1400, parentSize.height - 100);
mainPanel.setMaximumSize(maxSize);
mainPanel.setOpaque(false);
mainPanel.setOpaque(false);
// Big Image
addBigImageToStackModalPanel(mainPanel, si);
// Text
addTextToStackModalPanel(mainPanel,sa,si);
// Small images
int numSmallImages = 0;
// If current effect is a triggered/activated ability of an enchantment card, I want to show the enchanted card
GameEntityView enchantedEntityView = null;
Card hostCard = sa.getHostCard();
@@ -1308,45 +1326,45 @@ public final class CMatchUI
&& !sa.getRootAbility().getPaidList("Sacrificed").isEmpty()) {
// If the player activated its ability by sacrificing the enchantment, the enchantment has not anything attached anymore and the ex-enchanted card has to be searched in other ways.. for example, the green enchantment "Carapace"
enchantedEntity = sa.getRootAbility().getPaidList("Sacrificed").get(0).getEnchantingCard();
if(enchantedEntity != null) {
enchantedEntityView = enchantedEntity.getView();
if(enchantedEntity != null) {
enchantedEntityView = enchantedEntity.getView();
numSmallImages++;
}
}
}
// If current effect is a triggered ability, I want to show the triggering card if present
SpellAbility sourceSA = (SpellAbility) si.getTriggeringObject(AbilityKey.SourceSA);
CardView sourceCardView = null;
if(sourceSA != null) {
sourceCardView = sourceSA.getHostCard().getView();
numSmallImages++;
}
}
// I also want to show each type of targets (both cards and players)
List<GameEntityView> targets = getTargets(si,new ArrayList<GameEntityView>());
numSmallImages = numSmallImages + targets.size();
// Now I know how many small images - on to render them
if(enchantedEntityView != null) {
addSmallImageToStackModalPanel(enchantedEntityView,mainPanel,numSmallImages);
addSmallImageToStackModalPanel(enchantedEntityView,mainPanel,numSmallImages);
}
if(sourceCardView != null) {
addSmallImageToStackModalPanel(sourceCardView,mainPanel,numSmallImages);
addSmallImageToStackModalPanel(sourceCardView,mainPanel,numSmallImages);
}
for(GameEntityView gev : targets) {
addSmallImageToStackModalPanel(gev, mainPanel, numSmallImages);
}
FOptionPane.showOptionDialog(null, "Forge", null, mainPanel, ImmutableList.of(Localizer.getInstance().getMessage("lblOK")));
}
FOptionPane.showOptionDialog(null, "Forge", null, mainPanel, ImmutableList.of(Localizer.getInstance().getMessage("lblOK")));
// here the user closed the modal - time to update the next notifiable stack index
}
// In any case, I have to increase the counter
nextNotifiableStackIndex++;
} else {
// Not yet time to show the modal - schedule the method again, and try again later
Runnable tryAgainThread = new Runnable() {
@Override
@@ -1355,8 +1373,8 @@ public final class CMatchUI
}
};
GuiBase.getInterface().invokeInEdtLater(tryAgainThread);
}
}
}
private List<GameEntityView> getTargets(SpellAbilityStackInstance si, List<GameEntityView> result){
@@ -1380,22 +1398,22 @@ public final class CMatchUI
return getTargets(si.getSubInstance(),result);
}
private void addBigImageToStackModalPanel(JPanel mainPanel, SpellAbilityStackInstance si) {
StackItemView siv = si.getView();
int rotation = getRotation(si.getCardView());
FImagePanel imagePanel = new FImagePanel();
BufferedImage bufferedImage = FImageUtil.getImage(siv.getSourceCard().getCurrentState());
FImagePanel imagePanel = new FImagePanel();
BufferedImage bufferedImage = FImageUtil.getImage(siv.getSourceCard().getCurrentState());
imagePanel.setImage(bufferedImage, rotation, AutoSizeImageMode.SOURCE);
int imageWidth = 433;
int imageHeight = 600;
Dimension imagePanelDimension = new Dimension(imageWidth,imageHeight);
imagePanel.setMinimumSize(imagePanelDimension);
mainPanel.add(imagePanel, "cell 0 0, spany 3");
mainPanel.add(imagePanel, "cell 0 0, spany 3");
}
private void addTextToStackModalPanel(JPanel mainPanel, SpellAbility sa, SpellAbilityStackInstance si) {
String who = sa.getActivatingPlayer().getName();
String action = sa.isSpell() ? " cast " : sa.isTrigger() ? " triggered " : " activated ";
@@ -1409,45 +1427,45 @@ public final class CMatchUI
TargetChoices targets = si.getTargetChoices();
sb.append(targets);
}
sb.append(".");
sb.append(".");
String message1 = sb.toString();
String message2 = si.getStackDescription();
String message2 = si.getStackDescription();
String messageTotal = message1 + "\n\n" + message2;
final FTextArea prompt1 = new FTextArea(messageTotal);
prompt1.setFont(FSkin.getFont(21));
prompt1.setAutoSize(true);
prompt1.setMinimumSize(new Dimension(475,200));
mainPanel.add(prompt1, "cell 1 0, aligny top");
mainPanel.add(prompt1, "cell 1 0, aligny top");
}
private void addSmallImageToStackModalPanel(GameEntityView gameEntityView, JPanel mainPanel, int numTarget) {
if(gameEntityView instanceof CardView) {
CardView cardView = (CardView) gameEntityView;
int currRotation = getRotation(cardView);
int currRotation = getRotation(cardView);
FImagePanel targetPanel = new FImagePanel();
BufferedImage bufferedImage = FImageUtil.getImage(cardView.getCurrentState());
BufferedImage bufferedImage = FImageUtil.getImage(cardView.getCurrentState());
targetPanel.setImage(bufferedImage, currRotation, AutoSizeImageMode.SOURCE);
int imageWidth = 217;
int imageHeight = 300;
Dimension targetPanelDimension = new Dimension(imageWidth,imageHeight);
targetPanel.setMinimumSize(targetPanelDimension);
mainPanel.add(targetPanel, "cell 1 1, split " + numTarget+ ", aligny bottom");
mainPanel.add(targetPanel, "cell 1 1, split " + numTarget+ ", aligny bottom");
} else if(gameEntityView instanceof PlayerView) {
PlayerView playerView = (PlayerView) gameEntityView;
SkinImage playerAvatar = getPlayerAvatar(playerView, 0);
final FLabel lblIcon = new FLabel.Builder().icon(playerAvatar).build();
Dimension dimension = playerAvatar.getSizeForPaint(JOptionPane.getRootFrame().getGraphics());
mainPanel.add(lblIcon, "cell 1 1, split " + numTarget+ ", w " + dimension.getWidth() + ", h " + dimension.getHeight() + ", aligny bottom");
mainPanel.add(lblIcon, "cell 1 1, split " + numTarget+ ", w " + dimension.getWidth() + ", h " + dimension.getHeight() + ", aligny bottom");
}
}
}
private int getRotation(CardView cardView) {
final int rotation;
if (cardView.isSplitCard()) {
String cardName = cardView.getName();
if (cardName.isEmpty()) { cardName = cardView.getAlternateState().getName(); }
PaperCard pc = StaticData.instance().getCommonCards().getCard(cardName);
boolean hasKeywordAftermath = pc != null && Card.getCardForUi(pc).hasKeyword(Keyword.AFTERMATH);
@@ -1459,7 +1477,7 @@ public final class CMatchUI
return rotation;
}
@Override
public void notifyStackRemoval(GameEventSpellRemovedFromStack event) {
// I always decrease the counter
@@ -1474,49 +1492,49 @@ public final class CMatchUI
createLandPopupPanel(land);
}
};
GuiBase.getInterface().invokeInEdtAndWait(createPopupThread);
GuiBase.getInterface().invokeInEdtAndWait(createPopupThread);
}
private void createLandPopupPanel(Card land) {
String landPlayedNotificationPolicy = FModel.getPreferences().getPref(FPref.UI_LAND_PLAYED_NOTIFICATION_POLICY);
Player cardController = land.getController();
boolean isAi = cardController.isAI();
if(ForgeConstants.LAND_PLAYED_NOTIFICATION_ALWAYS.equals(landPlayedNotificationPolicy)
Player cardController = land.getController();
boolean isAi = cardController.isAI();
if(ForgeConstants.LAND_PLAYED_NOTIFICATION_ALWAYS.equals(landPlayedNotificationPolicy)
|| (ForgeConstants.LAND_PLAYED_NOTIFICATION_AI.equals(landPlayedNotificationPolicy) && (isAi))
|| (ForgeConstants.LAND_PLAYED_NOTIFICATION_ALWAYS_FOR_NONBASIC_LANDS.equals(landPlayedNotificationPolicy) && !land.isBasicLand())
|| (ForgeConstants.LAND_PLAYED_NOTIFICATION_AI_FOR_NONBASIC_LANDS.equals(landPlayedNotificationPolicy) && !land.isBasicLand()) && (isAi)) {
String title = "Forge";
String title = "Forge";
List<String> options = ImmutableList.of(Localizer.getInstance().getMessage("lblOK"));
MigLayout migLayout = new MigLayout("insets 15, left, gap 30, fill");
JPanel mainPanel = new JPanel(migLayout);
final Dimension parentSize = JOptionPane.getRootFrame().getSize();
Dimension maxSize = new Dimension(1400, parentSize.height - 100);
mainPanel.setMaximumSize(maxSize);
mainPanel.setOpaque(false);
mainPanel.setOpaque(false);
int rotation = getRotation(land.getView());
FImagePanel imagePanel = new FImagePanel();
BufferedImage bufferedImage = FImageUtil.getImage(land.getCurrentState().getView());
FImagePanel imagePanel = new FImagePanel();
BufferedImage bufferedImage = FImageUtil.getImage(land.getCurrentState().getView());
imagePanel.setImage(bufferedImage, rotation, AutoSizeImageMode.SOURCE);
int imageWidth = 433;
int imageHeight = 600;
Dimension imagePanelDimension = new Dimension(imageWidth,imageHeight);
imagePanel.setMinimumSize(imagePanelDimension);
mainPanel.add(imagePanel, "cell 0 0, spany 3");
String msg = cardController.toString() + " puts " + land.toString() + " into play into " + ZoneType.Battlefield.toString() + ".";
String msg = cardController.toString() + " puts " + land.toString() + " into play into " + ZoneType.Battlefield.toString() + ".";
final FTextArea prompt1 = new FTextArea(msg);
prompt1.setFont(FSkin.getFont(21));
prompt1.setAutoSize(true);
prompt1.setMinimumSize(new Dimension(475,200));
mainPanel.add(prompt1, "cell 1 0, aligny top");
FOptionPane.showOptionDialog(null, title, null, mainPanel, options);
}
mainPanel.add(prompt1, "cell 1 0, aligny top");
FOptionPane.showOptionDialog(null, title, null, mainPanel, options);
}
}
}

View File

@@ -0,0 +1,314 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2021 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;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import forge.game.GameEntityView;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.gui.SOverlayUtils;
import forge.toolbox.FButton;
import forge.toolbox.FLabel;
import forge.toolbox.FScrollPane;
import forge.toolbox.FSkin;
import forge.toolbox.FSkin.SkinnedPanel;
import forge.util.Localizer;
import forge.util.TextUtil;
import forge.view.FDialog;
import forge.view.arcane.CardPanel;
import net.miginfocom.swing.MigLayout;
/**
* 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 VAssignGenericAmount {
final Localizer localizer = Localizer.getInstance();
private final CMatchUI matchUI;
// Width and height of assign dialog
private final int wDlg = 700;
private final int hDlg = 500;
private final FDialog dlg = new FDialog();
// Amount storage
private final int totalAmountToAssign;
private final String lblAmount;
private final JLabel lblTotalAmount;
// Label Buttons
private final FButton btnOK = new FButton(localizer.getMessage("lblOk"));
private final FButton btnReset = new FButton(localizer.getMessage("lblReset"));
private static class AssignTarget {
public final GameEntityView entity;
public final JLabel label;
public final int max;
public int amount;
public AssignTarget(final GameEntityView e, final JLabel lbl, int max0) {
entity = e;
label = lbl;
max = max0;
amount = 0;
}
}
private final List<AssignTarget> targetsList = new ArrayList<>();
private final Map<GameEntityView, AssignTarget> targetsMap = new HashMap<>();
// Mouse actions
private final MouseAdapter mad = new MouseAdapter() {
@Override
public void mouseEntered(final MouseEvent evt) {
((CardPanel) evt.getSource()).setBorder(new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_ACTIVE), 2));
}
@Override
public void mouseExited(final MouseEvent evt) {
((CardPanel) evt.getSource()).setBorder((Border)null);
}
@Override
public void mousePressed(final MouseEvent evt) {
CardView source = ((CardPanel) 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)
assignAmountTo(source, meta, isLMB);
}
};
public VAssignGenericAmount(final CMatchUI matchUI, final CardView effectSource, final Map<GameEntityView, Integer> targets, final int amount, final boolean atLeastOne, final String amountLabel) {
this.matchUI = matchUI;
dlg.setTitle(localizer.getMessage("lbLAssignAmountForEffect", amountLabel, effectSource.toString()));
totalAmountToAssign = amount;
lblAmount = amountLabel;
lblTotalAmount = new FLabel.Builder().text(localizer.getMessage("lblTotalAmountText", lblAmount)).build();
// Top-level UI stuff
final JPanel overlay = SOverlayUtils.genericOverlay();
final SkinnedPanel pnlMain = new SkinnedPanel();
pnlMain.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2));
// Effect Source area
final CardPanel pnlSource = new CardPanel(matchUI, effectSource);
pnlSource.setOpaque(false);
pnlSource.setCardBounds(0, 0, 105, 150);
final JPanel pnlInfo = new JPanel(new MigLayout("insets 0, gap 0, wrap"));
pnlInfo.setOpaque(false);
pnlInfo.add(lblTotalAmount, "gap 0 0 20px 5px");
pnlInfo.add(new FLabel.Builder().text(localizer.getMessage("lblLClickAmountMessage", lblAmount)).build(), "gap 0 0 0 5px");
pnlInfo.add(new FLabel.Builder().text(localizer.getMessage("lblRClickAmountMessage", lblAmount)).build(), "gap 0 0 0 5px");
// Targets area
final JPanel pnlTargets = new JPanel();
pnlTargets.setOpaque(false);
int cols = targets.size();
final String wrap = "wrap " + cols;
pnlTargets.setLayout(new MigLayout("insets 0, gap 0, ax center, " + wrap));
final FScrollPane scrTargets = new FScrollPane(pnlTargets, false);
// Top row of cards...
for (final Map.Entry<GameEntityView, Integer> e : targets.entrySet()) {
int maxAmount = e.getValue() != null ? e.getValue() : amount;
final AssignTarget at = new AssignTarget(e.getKey(), new FLabel.Builder().text("0").fontSize(18).fontAlign(SwingConstants.CENTER).build(), maxAmount);
addPanelForTarget(pnlTargets, at);
}
// ... bottom row of labels.
for (final AssignTarget l : targetsList) {
pnlTargets.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) { resetAssignedAmount(); initialAssignAmount(atLeastOne); } });
// Final UI layout
pnlMain.setLayout(new MigLayout("insets 0, gap 0, wrap 2, ax center"));
pnlMain.add(pnlSource, "w 125px!, h 160px!, gap 50px 0 0 15px");
pnlMain.add(pnlInfo, "gap 20px 0 0 15px");
pnlMain.add(scrTargets, "w 96%!, gap 2% 0 0 0, pushy, growy, ax center, span 2");
final JPanel pnlButtons = new JPanel(new MigLayout("insets 0, gap 0, ax center"));
pnlButtons.setOpaque(false);
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);
initialAssignAmount(atLeastOne);
SOverlayUtils.showOverlay();
dlg.setUndecorated(true);
dlg.setContentPane(pnlMain);
dlg.setSize(new Dimension(wDlg, hDlg));
dlg.setLocation((overlay.getWidth() - wDlg) / 2, (overlay.getHeight() - hDlg) / 2);
dlg.setModalityType(ModalityType.APPLICATION_MODAL);
dlg.setVisible(true);
}
private void addPanelForTarget(final JPanel pnlTargets, final AssignTarget at) {
CardView cv = null;
if (at.entity instanceof CardView) {
cv = (CardView)at.entity;
} else if (at.entity instanceof PlayerView) {
final PlayerView p = (PlayerView)at.entity;
cv = new CardView(-1, null, at.entity.toString(), p, matchUI.getAvatarImage(p.getLobbyPlayerName()));
} else {
return;
}
final CardPanel cp = new CardPanel(matchUI, cv);
cp.setCardBounds(0, 0, 105, 150);
cp.setOpaque(true);
pnlTargets.add(cp, "w 145px!, h 170px!, gap 5px 5px 3px 3px, ax center");
cp.addMouseListener(mad);
targetsMap.put(cv, at);
targetsList.add(at);
}
private void assignAmountTo(CardView source, final boolean meta, final boolean isAdding) {
AssignTarget at = targetsMap.get(source);
int assigned = at.amount;
int leftToAssign = Math.max(0, at.max - assigned);
int amountToAdd = isAdding ? 1 : -1;
int remainingAmount = Math.min(getRemainingAmount(), leftToAssign);
// Left click adds, right click substracts.
// Hold Ctrl to assign to maximum amount
if (meta) {
if (isAdding) {
amountToAdd = leftToAssign > 0 ? leftToAssign : 0;
} else {
amountToAdd = -assigned;
}
}
if (amountToAdd > remainingAmount) {
amountToAdd = remainingAmount;
}
if (0 == amountToAdd || amountToAdd + assigned < 0) {
return;
}
addAssignedAmount(at, amountToAdd);
updateLabels();
}
private void initialAssignAmount(boolean atLeastOne) {
if (!atLeastOne) {
updateLabels();
return;
}
for(AssignTarget at : targetsList) {
addAssignedAmount(at, 1);
}
updateLabels();
}
private void resetAssignedAmount() {
for(AssignTarget at : targetsList)
at.amount = 0;
}
private void addAssignedAmount(final AssignTarget at, int addedAmount) {
// If we don't have enough left or we're trying to unassign too much return
final int canAssign = getRemainingAmount();
if (canAssign < addedAmount) {
addedAmount = canAssign;
}
at.amount = Math.max(0, addedAmount + at.amount);
}
private int getRemainingAmount() {
int spent = 0;
for(AssignTarget at : targetsList) {
spent += at.amount;
}
return totalAmountToAssign - spent;
}
/** Updates labels and other UI elements.*/
private void updateLabels() {
int amountLeft = totalAmountToAssign;
for ( AssignTarget at : targetsList )
{
amountLeft -= at.amount;
StringBuilder sb = new StringBuilder();
sb.append(at.amount);
if (at.max - at.amount == 0) {
sb.append(" (").append(localizer.getMessage("lblMax")).append(")");
}
at.label.setText(sb.toString());
}
lblTotalAmount.setText(TextUtil.concatNoSpace(localizer.getMessage("lblAvailableAmount", lblAmount), ": " , String.valueOf(amountLeft), " (of ", String.valueOf(totalAmountToAssign), ")"));
btnOK.setEnabled(amountLeft == 0);
}
private void finish() {
if ( getRemainingAmount() > 0 )
return;
dlg.dispose();
SOverlayUtils.hideOverlay();
}
public Map<GameEntityView, Integer> getAssignedMap() {
Map<GameEntityView, Integer> result = new HashMap<>(targetsList.size());
for (AssignTarget at : targetsList)
result.put(at.entity, at.amount);
return result;
}
}

View File

@@ -146,6 +146,12 @@ public class PlayerControllerForTests extends PlayerController {
throw new IllegalStateException("Erring on the side of caution here...");
}
@Override
public Map<GameEntity, Integer> divideShield(Card effectSource, Map<GameEntity, Integer> affected, int shieldAmount) {
throw new IllegalStateException("Erring on the side of caution here...");
}
@Override
public Integer announceRequirements(SpellAbility ability, String announce) {
throw new IllegalStateException("Erring on the side of caution here...");

View File

@@ -49,6 +49,7 @@ import forge.model.FModel;
import forge.player.PlayerZoneUpdate;
import forge.player.PlayerZoneUpdates;
import forge.screens.match.views.VAssignCombatDamage;
import forge.screens.match.views.VAssignGenericAmount;
import forge.screens.match.views.VPhaseIndicator;
import forge.screens.match.views.VPhaseIndicator.PhaseLabel;
import forge.screens.match.views.VPlayerPanel;
@@ -395,6 +396,18 @@ public class MatchController extends AbstractGuiGame {
}.invokeAndWait();
}
@Override
public Map<GameEntityView, Integer> assignGenericAmount(final CardView effectSource, final Map<GameEntityView, Integer> targets,
final int amount, final boolean atLeastOne, final String amountLabel) {
return new WaitCallback<Map<GameEntityView, Integer>>() {
@Override
public void run() {
final VAssignGenericAmount v = new VAssignGenericAmount(effectSource, targets, amount, atLeastOne, amountLabel, this);
v.show();
}
}.invokeAndWait();
}
@Override
public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) {
for (final PlayerView p : manaPoolUpdate) {

View File

@@ -0,0 +1,348 @@
/*
* 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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.badlogic.gdx.utils.Align;
import forge.Forge;
import forge.Graphics;
import forge.assets.FImage;
import forge.assets.FSkinColor;
import forge.assets.FSkinColor.Colors;
import forge.assets.FSkinFont;
import forge.assets.FSkinImage;
import forge.card.CardZoom;
import forge.game.GameEntityView;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.screens.match.MatchController;
import forge.toolbox.FCardPanel;
import forge.toolbox.FContainer;
import forge.toolbox.FDialog;
import forge.toolbox.FDisplayObject;
import forge.toolbox.FEvent;
import forge.toolbox.FEvent.FEventHandler;
import forge.toolbox.FLabel;
import forge.toolbox.FOptionPane;
import forge.toolbox.FScrollPane;
import forge.util.Callback;
import forge.util.CardTranslation;
import forge.util.Localizer;
import forge.util.TextUtil;
import forge.util.Utils;
import forge.util.WaitCallback;
public class VAssignGenericAmount extends FDialog {
private static final float CARD_GAP_X = Utils.scale(10);
private static final float ADD_BTN_HEIGHT = Utils.AVG_FINGER_HEIGHT * 0.75f;
private final Callback<Map<GameEntityView, Integer>> callback;
private final int totalAmountToAssign;
private final String lblAmount;
private final FLabel lblTotalAmount;
private final EffectSourcePanel pnlSource;
private final TargetsPanel pnlTargets;
private final List<AssignTarget> targetsList = new ArrayList<>();
private final Map<GameEntityView, AssignTarget> targetsMap = new HashMap<>();
/** Constructor.
*
* @param attacker0 {@link forge.game.card.Card}
* @param targets Map<GameEntity, Integer>, map of GameEntity and its maximum assignable amount
* @param amount Total amount to be assigned
* @param atLeastOne Must assign at least one amount to each target
*/
public VAssignGenericAmount(final CardView effectSource, final Map<GameEntityView, Integer> targets, final int amount, final boolean atLeastOne, final String amountLabel, final WaitCallback<Map<GameEntityView, Integer>> waitCallback) {
super(Localizer.getInstance().getMessage("lbLAssignAmountForEffect", amountLabel, CardTranslation.getTranslatedName(effectSource.getName())) , 2);
callback = waitCallback;
totalAmountToAssign = amount;
lblAmount = amountLabel;
lblTotalAmount = add(new FLabel.Builder().text(Localizer.getInstance().getMessage("lblTotalAmountText", lblAmount)).align(Align.center).build());
pnlSource = add(new EffectSourcePanel(effectSource));
pnlTargets = add(new TargetsPanel(targets));
initButton(0, Localizer.getInstance().getMessage("lblOK"), new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
finish();
}
});
initButton(1, Localizer.getInstance().getMessage("lblReset"), new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
resetAssignedDamage();
initialAssignAmount(atLeastOne);
}
});
initialAssignAmount(atLeastOne);
}
@Override
protected float layoutAndGetHeight(float width, float maxHeight) {
float padding = FOptionPane.PADDING;
float w = width - 2 * padding;
float x = padding;
float labelHeight = lblTotalAmount.getAutoSizeBounds().height;
float y = maxHeight - labelHeight + padding;
float dtOffset = ADD_BTN_HEIGHT + targetsList.get(0).label.getAutoSizeBounds().height;
float cardPanelHeight = (y - dtOffset - labelHeight - 3 * padding) / 2;
float cardPanelWidth = cardPanelHeight / FCardPanel.ASPECT_RATIO;
y = padding;
pnlSource.setBounds(x + (w - cardPanelWidth) / 2, y, cardPanelWidth, cardPanelHeight);
y += cardPanelHeight + padding;
lblTotalAmount.setBounds(x, y, w, labelHeight);
y += labelHeight + padding;
pnlTargets.setBounds(0, y, width, cardPanelHeight + dtOffset);
return maxHeight;
}
private class TargetsPanel extends FScrollPane {
private TargetsPanel(final Map<GameEntityView, Integer> targets) {
for (final Map.Entry<GameEntityView, Integer> e : targets.entrySet()) {
addDamageTarget(e.getKey(), e.getValue());
}
}
private void addDamageTarget(GameEntityView entity, int max) {
AssignTarget at = add(new AssignTarget(entity, max));
targetsMap.put(entity, at);
targetsList.add(at);
}
@Override
protected ScrollBounds layoutAndGetScrollBounds(float visibleWidth, float visibleHeight) {
float cardPanelHeight = visibleHeight - ADD_BTN_HEIGHT - targetsList.get(0).label.getAutoSizeBounds().height;
float width = cardPanelHeight / FCardPanel.ASPECT_RATIO;
float dx = width + CARD_GAP_X;
float x = (visibleWidth - targetsList.size() * dx + CARD_GAP_X) / 2;
if (x < FOptionPane.PADDING) {
x = FOptionPane.PADDING;
}
for (AssignTarget at : targetsList) {
at.setBounds(x, 0, width, visibleHeight);
x += dx;
}
return new ScrollBounds(x - CARD_GAP_X + FOptionPane.PADDING, visibleHeight);
}
}
private class AssignTarget extends FContainer {
private final GameEntityView entity;
private final FDisplayObject obj;
private final FLabel label, btnSubtract, btnAdd;
private final int max;
private int amount;
public AssignTarget(GameEntityView entity0, int max0) {
entity = entity0;
max = max0;
if (entity instanceof CardView) {
obj = add(new EffectSourcePanel((CardView)entity));
}
else if (entity instanceof PlayerView) {
PlayerView player = (PlayerView)entity;
obj = add(new MiscTargetPanel(player.getName(), MatchController.getPlayerAvatar(player)));
}
else {
obj = add(new MiscTargetPanel(entity.toString(), FSkinImage.UNKNOWN));
}
label = add(new FLabel.Builder().text("0").font(FSkinFont.get(18)).align(Align.center).build());
btnSubtract = add(new FLabel.ButtonBuilder().icon(FSkinImage.MINUS).command(new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
assignAmountTo(entity, false);
}
}).build());
btnAdd = add(new FLabel.ButtonBuilder().icon(Forge.hdbuttons ? FSkinImage.HDPLUS : FSkinImage.PLUS).command(new FEventHandler() {
@Override
public void handleEvent(FEvent e) {
assignAmountTo(entity, true);
}
}).build());
}
@Override
protected void doLayout(float width, float height) {
float y = 0;
obj.setBounds(0, y, width, FCardPanel.ASPECT_RATIO * width);
y += obj.getHeight();
label.setBounds(0, y, width, label.getAutoSizeBounds().height);
y += label.getHeight();
float buttonSize = (width - FOptionPane.PADDING) / 2;
btnSubtract.setBounds(0, y, buttonSize, ADD_BTN_HEIGHT);
btnAdd.setBounds(width - buttonSize, y, buttonSize, ADD_BTN_HEIGHT);
}
}
private static class EffectSourcePanel extends FCardPanel {
private EffectSourcePanel(CardView card) {
super(card);
}
@Override
public boolean tap(float x, float y, int count) {
CardZoom.show(getCard());
return true;
}
@Override
public boolean longPress(float x, float y) {
CardZoom.show(getCard());
return true;
}
@Override
protected float getPadding() {
return 0;
}
}
private static class MiscTargetPanel extends FDisplayObject {
private static final FSkinFont FONT = FSkinFont.get(18);
private static final FSkinColor FORE_COLOR = FSkinColor.get(Colors.CLR_TEXT);
private final String name;
private final FImage image;
private MiscTargetPanel(String name0, FImage image0) {
name = name0;
image = image0;
}
@Override
public void draw(Graphics g) {
float w = getWidth();
float h = getHeight();
g.drawImage(image, 0, 0, w, w);
g.drawText(name, FONT, FORE_COLOR, 0, w, w, h - w, false, Align.center, true);
}
}
private void assignAmountTo(GameEntityView source, boolean isAdding) {
AssignTarget at = targetsMap.get(source);
int assigned = at.amount;
int leftToAssign = Math.max(0, at.max - assigned);
int amountToAdd = isAdding ? 1 : -1;
int remainingAmount = Math.min(getRemainingAmount(), leftToAssign);
if (amountToAdd > remainingAmount) {
amountToAdd = remainingAmount;
}
if (0 == amountToAdd || amountToAdd + assigned < 0) {
return;
}
addAssignedAmount(at, amountToAdd);
updateLabels();
}
private void initialAssignAmount(boolean atLeastOne) {
if (!atLeastOne) {
updateLabels();
return;
}
for(AssignTarget at : targetsList) {
addAssignedAmount(at, 1);
}
updateLabels();
}
private void resetAssignedDamage() {
for (AssignTarget at : targetsList) {
at.amount = 0;
}
}
private void addAssignedAmount(final AssignTarget at, int addedAmount) {
// If we don't have enough left or we're trying to unassign too much return
int canAssign = getRemainingAmount();
if (canAssign < addedAmount) {
addedAmount = canAssign;
}
at.amount = Math.max(0, addedAmount + at.amount);
}
private int getRemainingAmount() {
int spent = 0;
for (AssignTarget at : targetsList) {
spent += at.amount;
}
return totalAmountToAssign - spent;
}
/** Updates labels and other UI elements.*/
private void updateLabels() {
int amountLeft = totalAmountToAssign;
for (AssignTarget at : targetsList) {
amountLeft -= at.amount;
StringBuilder sb = new StringBuilder();
sb.append(at.amount);
if (at.max - at.amount == 0) {
sb.append(" (").append(Localizer.getInstance().getMessage("lblMax")).append(")");
}
at.label.setText(sb.toString());
}
lblTotalAmount.setText(TextUtil.concatNoSpace(Localizer.getInstance().getMessage("lblAvailableAmount", lblAmount) + ": ",
String.valueOf(amountLeft), " (of ", String.valueOf(totalAmountToAssign), ")"));
setButtonEnabled(0, amountLeft == 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 (getRemainingAmount() > 0) {
return;
}
hide();
callback.run(getAssignedMap());
}
public Map<GameEntityView, Integer> getAssignedMap() {
Map<GameEntityView, Integer> result = new HashMap<>(targetsList.size());
for (AssignTarget at : targetsList)
result.put(at.entity, at.amount);
return result;
}
}

View File

@@ -0,0 +1,13 @@
Name:Divine Deflection
ManaCost:X W
Types:Instant
A:SP$ StoreSVar | Cost$ X W | SVar$ ShieldAmount | Type$ Calculate | Expression$ X | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Prevent the next X damage that would be dealt to you and/or permanents you control this turn. If damage is prevented this way, CARDNAME deals that much damage to any target.
SVar:DBEffect:DB$ Effect | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target to deal prevented damage to | ReplacementEffects$ ReDamage | RememberObjects$ Targeted
SVar:ReDamage:Event$ DamageDone | ActiveZones$ Command | ValidTarget$ You,Permanent.YouCtrl | ReplaceWith$ PreventDamage | PreventionEffect$ NextN | Description$ Prevent the next X damage that would be dealt to you and/or permanents you control this turn. If damage is prevented this way, EFFECTSOURCE deals that much damage to any target.
SVar:PreventDamage:DB$ ReplaceDamage | Amount$ ShieldAmount | DivideShield$ True | SubAbility$ DBDealDamage
SVar:DBDealDamage:DB$ DealDamage | NumDmg$ Y | Defined$ Remembered | DamageSource$ EffectSource
SVar:X:Count$xPaid
SVar:ShieldAmount:Number$0
SVar:Y:ReplaceCount$DamageAmount
AI:RemoveDeck:All
Oracle:Prevent the next X damage that would be dealt to you and/or permanents you control this turn. If damage is prevented this way, Divine Deflection deals that much damage to any target.

View File

@@ -0,0 +1,11 @@
Name:Harm's Way
ManaCost:W
Types:Instant
A:SP$ ChooseSource | Cost$ W | Choices$ Card,Emblem | AILogic$ NeedsPrevention | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ The next 2 damage that a source of your choice would deal to you and/or permanents you control this turn is dealt to any target instead.
SVar:DBEffect:DB$ Effect | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target to redirect damage to | ReplacementEffects$ ReDamage | RememberObjects$ Targeted | SubAbility$ DBCleanup
SVar:ReDamage:Event$ DamageDone | ActiveZones$ Command | ValidTarget$ You,Permanent.YouCtrl | ValidSource$ Card.ChosenCard,Emblem.ChosenCard | ReplaceWith$ SplitDamage | DamageTarget$ Remembered | Description$ The next 2 damage that a source of your choice would deal to you and/or permanents you control this turn is dealt to any target instead.
SVar:SplitDamage:DB$ ReplaceSplitDamage | DamageTarget$ Remembered | VarName$ ShieldAmount | DivideShield$ True
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
SVar:ShieldAmount:Number$2
AI:RemoveDeck:All
Oracle:The next 2 damage that a source of your choice would deal to you and/or permanents you control this turn is dealt to any target instead.

View File

@@ -0,0 +1,15 @@
Name:Refraction Trap
ManaCost:3 W
Types:Instant Trap
SVar:AltCost:Cost$ W | CheckSVar$ X | SVarCompare$ GE1 | Description$ If an opponent cast a red instant or sorcery spell this turn, you may pay {W} rather than pay this spell's mana cost.
A:SP$ ChooseSource | Cost$ 3 W | Choices$ Card,Emblem | AILogic$ NeedsPrevention | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Prevent the next 3 damage that a source of your choice would deal to you and/or permanents you control this turn. If damage is prevented this way, CARDNAME deals that much damage to any target.
SVar:DBEffect:DB$ Effect | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target to deal prevented damage to | ReplacementEffects$ ReDamage | RememberObjects$ Targeted | SubAbility$ DBCleanup
SVar:ReDamage:Event$ DamageDone | ActiveZones$ Command | ValidTarget$ You,Permanent.YouCtrl | ValidSource$ Card.ChosenCard,Emblem.ChosenCard | ReplaceWith$ PreventDamage | PreventionEffect$ NextN | Description$ Prevent the next 3 damage that a source of your choice would deal to you and/or permanents you control this turn. If damage is prevented this way, EFFECTSOURCE deals that much damage to any target.
SVar:PreventDamage:DB$ ReplaceDamage | Amount$ ShieldAmount | DivideShield$ True | SubAbility$ DBDealDamage
SVar:DBDealDamage:DB$ DealDamage | NumDmg$ Y | Defined$ Remembered | DamageSource$ EffectSource
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
SVar:X:Count$ThisTurnCast_Instant.Red+OppCtrl,Sorcery.Red+OppCtrl
SVar:ShieldAmount:Number$3
SVar:Y:ReplaceCount$DamageAmount
AI:RemoveDeck:All
Oracle:If an opponent cast a red instant or sorcery spell this turn, you may pay {W} rather than pay this spell's mana cost.\nPrevent the next 3 damage that a source of your choice would deal to you and/or permanents you control this turn. If damage is prevented this way, Refraction Trap deals that much damage to any target.

View File

@@ -0,0 +1,16 @@
Name:Shining Shoal
ManaCost:X W W
Types:Instant Arcane
SVar:AltCost:Cost$ ExileFromHand<1/Card.White+Other/white card> | Description$ You may exile a white card with mana value X from your hand rather than pay this spell's mana cost.
A:SP$ ChooseSource | Cost$ X W W | Choices$ Card,Emblem | AILogic$ NeedsPrevention | SubAbility$ DBStoreSVar | StackDescription$ SpellDescription | SpellDescription$ The next X damage that a source of your choice would deal to you and/or creatures you control this turn is dealt to any target instead.
SVar:DBStoreSVar:DB$ StoreSVar | SVar$ ShieldAmount | Type$ Calculate | Expression$ Z | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target to redirect damage to | ReplacementEffects$ ReDamage | RememberObjects$ Targeted | SubAbility$ DBCleanup
SVar:ReDamage:Event$ DamageDone | ActiveZones$ Command | ValidTarget$ You,Creature.YouCtrl | ValidSource$ Card.ChosenCard,Emblem.ChosenCard | ReplaceWith$ SplitDamage | DamageTarget$ Remembered | Description$ The next X damage that a source of your choice would deal to you and/or creatures you control this turn is dealt to any target instead.
SVar:SplitDamage:DB$ ReplaceSplitDamage | DamageTarget$ Remembered | VarName$ ShieldAmount | DivideShield$ True
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
SVar:X:Count$xPaid
SVar:Y:Exiled$CardManaCost
SVar:Z:SVar$Y/Plus.X
SVar:ShieldAmount:Number$0
AI:RemoveDeck:All
Oracle:You may exile a white card with mana value X from your hand rather than pay this spell's mana cost.\nThe next X damage that a source of your choice would deal to you and/or creatures you control this turn is dealt to any target instead.

View File

@@ -361,6 +361,15 @@ lblTotalDamageText=Gesamtschaden: Unbekannt
lblAssignRemainingText=Verteile verbleibenden Schaden unter tödlich Verwundeten
lblLethal=Tödlich
lblAvailableDamagePoints=Verfügbare Schadenspunkte
#VAssignGenericAmount.java
# The {0} below should be amount label (like "shield" or "damage"), and {1} will be the name of the effect source
lbLAssignAmountForEffect=Assign {0} by {1}
lblLClickAmountMessage=Left click: Assign 1 {0}. (Left Click + Control): Assign maximum {0} points
lblRClickAmountMessage=Right click: Unassign 1 {0}. (Right Click + Control): Unassign all {0} points.
lblTotalAmountText=Available {0} points: Unknown
lblAvailableAmount=Available {0} points
lblMax=Max
lblShield=shield
#KeyboardShortcuts.java
lblSHORTCUT_SHOWSTACK=Duell: Zeige Stapelfenster
lblSHORTCUT_SHOWCOMBAT=Duell: Zeige Kampffenster
@@ -416,7 +425,7 @@ lblSelectOpponentDeck=Wähle das Deck des Gegners
lblGenerateNewDeck=Erzeuge ein neues Deck
lblRandomTheme=Zufälliges Deck
lblTestDeck=Teste Deck
lblLoading=Lade
lblLoading=Lade
#GameType.java
lblSealed=Sealed
lblDraft=Draft
@@ -1131,7 +1140,7 @@ lblSwipeUpTo=Wische hoch für %s
lblSwipeDownDetailView=Wische runter für Detailansicht
lblSwipeDownPictureView=Wische runter für Bildansicht
#VGameMenu.java
lblShowWinLoseOverlay=Zeige Sieg/Verlust-Anzeige
lblShowWinLoseOverlay=Zeige Sieg/Verlust-Anzeige
lblNoPlayerPriorityNoDeckListViewed=Kein Spieler hat Priorität, daher keine Decklistenanzeige möglich.
#FilesPage.java
lblFiles=Dateien
@@ -2276,7 +2285,7 @@ lblConquestName=Eroberung-Name
#HumanCostDecision.java
lblChooseXValueForCard={0} - Wähle Wert für X
lblSelectOneSameNameCardToDiscardAlreadyChosen=Wähle eine Karte mit dem gleichen Namen zum Abwerfen. Bereits gewählt:
lblSelectOneDifferentNameCardToDiscardAlreadyChosen=Wähle eine Karte mit einem anderen Namen zum Abwerfen. Bereits gewählt:
lblSelectOneDifferentNameCardToDiscardAlreadyChosen=Wähle eine Karte mit einem anderen Namen zum Abwerfen. Bereits gewählt:
lblSelectNMoreTargetTypeCardToDiscard=Wähle {0} weitere {1} zum Abwerfen.
lblDoYouWantCardDealNDamageToYou=Möchtest du, daß {0} dir {1} Schaden zufügt?
lblDrawNCardsConfirm=Ziehe {0} Karte(n)?
@@ -2672,7 +2681,7 @@ lblDetectedInvalidHostAddress=Ungültige Host-Adresse ({0}) wurde festgestellt.
#Player.java
lblChooseACompanion=Wähle einen Gefährten
lblChooseAColorFor=Wähle eine Farbe für {0}
lblRevealFaceDownCards=Enthülle verdeckte Karten von
lblRevealFaceDownCards=Enthülle verdeckte Karten von
lblLearnALesson=Lerne eine Lektion
#QuestPreferences.java
lblWildOpponentNumberError=Anzahl der Wild-Gegner kann nur 0 bis 3 sein

View File

@@ -18,8 +18,8 @@ lblerrLoadingLayoutFile=Your %s layout file could not be read. It will be delete
lblLoadingQuest=Loading quest...
#FScreen.java
#translate lblHomeWithSpaces,lblDeckEditorWithSpaces need keep spaces in text
lblHomeWithSpaces=Home
lblDeckEditorWithSpaces=Deck Editor
lblHomeWithSpaces=Home
lblDeckEditorWithSpaces=Deck Editor
lblWorkshop=Workshop
lblBacktoHome=Back to Home
lblCloseEditor=Close Editor
@@ -52,7 +52,7 @@ btnResetJavaFutureCompatibilityWarnings=Reset Java Compatibility Warnings
btnClearImageCache=Clear Image Cache
btnTokenPreviewer=Token Previewer
btnCopyToClipboard=Copy to Clipboard
cbpAutoUpdater=Auto updater
cbpAutoUpdater=Auto updater
nlAutoUpdater=Select the release channel to use for updating Forge
cbpSelectLanguage=Language
nlSelectLanguage=Select Language (Excluded Game part. Still a work in progress) (RESTART REQUIRED)
@@ -361,6 +361,15 @@ lblTotalDamageText=Available damage points: Unknown
lblAssignRemainingText=Distribute the remaining damage points among lethally wounded entities
lblLethal=Lethal
lblAvailableDamagePoints=Available damage points
#VAssignGenericAmount.java
# The {0} below should be amount label (like "shield" or "damage"), and {1} will be the name of the effect source
lbLAssignAmountForEffect=Assign {0} by {1}
lblLClickAmountMessage=Left click: Assign 1 {0}. (Left Click + Control): Assign maximum {0} points
lblRClickAmountMessage=Right click: Unassign 1 {0}. (Right Click + Control): Unassign all {0} points.
lblTotalAmountText=Available {0} points: Unknown
lblAvailableAmount=Available {0} points
lblMax=Max
lblShield=shield
#KeyboardShortcuts.java
lblSHORTCUT_SHOWSTACK=Match: show stack panel
lblSHORTCUT_SHOWCOMBAT=Match: show combat panel
@@ -416,7 +425,7 @@ lblSelectOpponentDeck=Select Opponent''s Deck
lblGenerateNewDeck=Generate New Deck
lblRandomTheme=Random Theme
lblTestDeck=Test Deck
lblLoading=Loading
lblLoading=Loading
#GameType.java
lblSealed=Sealed
lblDraft=Draft
@@ -763,8 +772,8 @@ lblWinsperDraftRotation=Wins per Draft Rotation
ttWinsperDraftRotation=If a Draft is not played for this many match wins, it will be removed or replaced.
lblRotationType=Rotation Type
ttRotationType=If set to 0, old drafts disappear, if set to 1, they are replaced with another one using different sets.
lblWildOpponentNumber=Number of Wild Opponents
lblWildOpponentMultiplier=Wild Multiplier
lblWildOpponentNumber=Number of Wild Opponents
lblWildOpponentMultiplier=Wild Multiplier
#StatTypeFilter.java
lblclicktotoogle=click to toggle the filter, right-click to show only
#SItemManagerUtil.java
@@ -1193,7 +1202,7 @@ lblSelectAttackTarget= or select player/planeswalker you wish to attack.
lblSelectBandingTarget= To attack as a band, select an attacking creature to activate its ''band'' then select another to join it.
#InputBlock.java
lblSelectBlockTarget=Select another attacker to declare blockers for.
lblSelectBlocker=Select creatures to block
lblSelectBlocker=Select creatures to block
lblOrSelectBlockTarget= or select another attacker to declare blockers for.
lblMorph=Morph
#PlayerControllerHuman.java
@@ -1569,9 +1578,9 @@ lblCreature=Creature
btnRestartRound=Restart Round
btnTournamentInfo=Tournament Info
btnNextRound=Next Round
btnWonRound=YOU HAVE WON ROUND
btnWonRound=YOU HAVE WON ROUND
btnWonTournament=***CONGRATULATIONS! YOU HAVE WON THE TOURNAMENT!***
btnLoseRound=YOU HAVE LOST ON ROUND
btnLoseRound=YOU HAVE LOST ON ROUND
btnQuit=Quit
btnContinue=Continue
btnRestart=Restart
@@ -1588,8 +1597,8 @@ lblFailedGauntlet=You have failed to pass the gauntlet.
lblLeaveTournamentDraftWarning1=If you leave now, this tournament will be forever gone.\nYou will keep the cards you drafted, but will receive no other prizes.\n\nWould you still like to quit the tournament?
lblLeaveTournamentDraftWarning2=You have matches left to play!\nLeaving the tournament early will forfeit your potential future winnings.\nYou will still receive winnings as if you conceded your next match and you will keep the cards you drafted.\n\nWould you still like to quit the tournament?
lblReallyQuit=Really Quit?
lblForPlacing=For placing
lblHaveBeAward=, you have been awarded
lblForPlacing=For placing
lblHaveBeAward=, you have been awarded
lblTournamentReward=Tournament Reward
lblParticipateingTournamentReward=For participating in the tournament, you have been awarded the following promotional card:
lblCreditsAwarded=Credits Awarded
@@ -1608,22 +1617,22 @@ lblWouldLikeSaveDraft=Would you like to save this draft to the regular draft mod
lblSaveDraft=Save Draft
lblNoAvailableDraftsMessage=You do not have any draft-able sets unlocked!\nCome back later when you''ve unlocked more sets.
lblNoAvailableDrafts=No Available Drafts
lblEntryFeeOfDraftTournament=The entry fee for this booster draft tournament is
lblEntryFeeOfDraftTournament=The entry fee for this booster draft tournament is
lblWouldLikeCreateTournament= credits.\nWould you like to spend a token and create this tournament?
lblCreatingDraftTournament=Creating a Booster Draft Tournament
lblUnexpectedCreatingDraftTournament=Unexpected error when creating a draft tournament
lblUnexpectedCreatingDraftTournament=Unexpected error when creating a draft tournament
lblPleaseReportBug=. Please report this as a bug.
lbl1stPlace=1st Place:
lbl2ndPlace=2nd Place:
lbl3rdPlace=3rd Place:
lbl4thPlace=4th Place:
lbl1stPlace=1st Place:
lbl2ndPlace=2nd Place:
lbl3rdPlace=3rd Place:
lbl4thPlace=4th Place:
lblTime= time
lblCollectPrizes=Collect Prizes
lblCurrentlyInDraft=You are currently in a draft.\nYou should leave or finish that draft before starting another.
lblYouNeed=You need
lblYouNeed=You need
lblMoreCredits=more credits to enter this tournament.
lblNotEnoughCredits=Not Enough Credits
lblTournamentCosts=This tournament costs
lblTournamentCosts=This tournament costs
lblSureEnterTournament= credits to enter.\nAre you sure you wish to enter?
lblEnterDraftTournament=Enter Draft Tournament?
lblLeaveDraftConfirm=This will end the current draft and you will not be able to join this tournament again.\nYour credits will be refunded and the draft will be removed.\n\nLeave anyway?
@@ -1683,7 +1692,7 @@ lblWinsForRankIncrease=Wins For Rank Increase
lblWinsForMediumAI=Wins For Medium AI
lblWinsForHardAI=Wins For Hard AI
lblWinsForExpertAI=Wins For Expert AI
lblSaveFailed=Save Failed -
lblSaveFailed=Save Failed -
#QuestSpellShopScreen.java
lblMaximumSellingCredits=Maximum selling price is %d credits.
lblSellCardsAt=Selling cards at
@@ -2274,8 +2283,8 @@ lblHistoriiansWillRecallYourConquestAs=Historians will recall your conquest as:
lblConquestName=Conquest Name
#HumanCostDecision.java
lblChooseXValueForCard={0} - Choose a Value for X
lblSelectOneSameNameCardToDiscardAlreadyChosen=Select one of the cards with the same name to discard. Already chosen:
lblSelectOneDifferentNameCardToDiscardAlreadyChosen=Select one of the cards with a different name to discard. Already chosen:
lblSelectOneSameNameCardToDiscardAlreadyChosen=Select one of the cards with the same name to discard. Already chosen:
lblSelectOneDifferentNameCardToDiscardAlreadyChosen=Select one of the cards with a different name to discard. Already chosen:
lblSelectNMoreTargetTypeCardToDiscard=Select {0} more {1} to discard.
lblDoYouWantCardDealNDamageToYou=Do you want {0} to deal {1} damage to you?
lblDrawNCardsConfirm=Draw {0} Card(s)?
@@ -2312,7 +2321,7 @@ lblRemoveNTargetCounterFromCardPayCostConfirm=Pay Cost: Remove {0} {1} counter f
lblRemoveCountersFromAInZoneCard=Remove counter(s) from a card in {0}
lblSacrificeCardConfirm=Sacrifice {0}?
lblSelectATargetToSacrifice=Select a {0} to sacrifice ({1} left)
lblSelectOneOfCardsToTapAlreadyChosen=Select one of the cards to tap. Already chosen:
lblSelectOneOfCardsToTapAlreadyChosen=Select one of the cards to tap. Already chosen:
lblSelectACreatureToTap=Select a creature to tap.
lblEnoughValidCardNotToPayTheCost=Not enough valid cards left to tap to pay the cost.
lblCostPaymentInvalid=Cost payment invalid
@@ -2671,7 +2680,7 @@ lblDetectedInvalidHostAddress=Invalid host address ({0}) was detected.
#Player.java
lblChooseACompanion=Choose a companion
lblChooseAColorFor=Choose a color for {0}
lblRevealFaceDownCards=Revealing face-down cards from
lblRevealFaceDownCards=Revealing face-down cards from
lblLearnALesson=Learn a Lesson
#QuestPreferences.java
lblWildOpponentNumberError=Wild Opponents can only be 0 to 3

View File

@@ -18,8 +18,8 @@ lblerrLoadingLayoutFile=No se pudo leer el archivo de diseño %s. Se eliminará
lblLoadingQuest=Cargando datos de aventura....
#FScreen.java
#translate lblHomeWithSpaces,lblDeckEditorWithSpaces need keep spaces in text
lblHomeWithSpaces=Inicio
lblDeckEditorWithSpaces=Editor de mazos
lblHomeWithSpaces=Inicio
lblDeckEditorWithSpaces=Editor de mazos
lblWorkshop=Taller
lblBacktoHome=Volver a Inicio
lblCloseEditor=Cerrar editor
@@ -361,6 +361,15 @@ lblTotalDamageText=Puntos de daño disponibles: Desconocido
lblAssignRemainingText=Distribuye los puntos de daño restantes entre las entidades letalmente heridas.
lblLethal=Letal
lblAvailableDamagePoints=Puntos de daño disponibles
#VAssignGenericAmount.java
# The {0} below should be amount label (like "shield" or "damage"), and {1} will be the name of the effect source
lbLAssignAmountForEffect=Assign {0} by {1}
lblLClickAmountMessage=Left click: Assign 1 {0}. (Left Click + Control): Assign maximum {0} points
lblRClickAmountMessage=Right click: Unassign 1 {0}. (Right Click + Control): Unassign all {0} points.
lblTotalAmountText=Available {0} points: Unknown
lblAvailableAmount=Available {0} points
lblMax=Max
lblShield=shield
#KeyboardShortcuts.java
lblSHORTCUT_SHOWSTACK=Partida: mostrar panel de pila
lblSHORTCUT_SHOWCOMBAT=Partida: mostrar panel de combate
@@ -416,7 +425,7 @@ lblSelectOpponentDeck=Seleccionar mazo del oponente
lblGenerateNewDeck=Generar nuevo mazo
lblRandomTheme=Tema aleatorio
lblTestDeck=Probar mazo
lblLoading=Cargando
lblLoading=Cargando
#GameType.java
lblSealed=Sellado
lblDraft=Draft
@@ -763,8 +772,8 @@ lblWinsperDraftRotation=Victorias rotación de Draft
ttWinsperDraftRotation=Si no se juega un Draft para esta cantidad de victorias, se eliminará o reemplazará.
lblRotationType=Tipo de rotación
ttRotationType=Si se establece en 0, los anteriores Draft desaparecen, si se establece en 1, se reemplazan por otros que utilizan sets diferentes.
lblWildOpponentNumber=Número de oponentes salvajes
lblWildOpponentMultiplier=Multiplicador salvaje
lblWildOpponentNumber=Número de oponentes salvajes
lblWildOpponentMultiplier=Multiplicador salvaje
#StatTypeFilter.java
lblclicktotoogle=haz clic para alternar el filtro, haz clic con el botón derecho para solo mostrar
#SItemManagerUtil.java
@@ -1569,9 +1578,9 @@ lblCreature=Criatura
btnRestartRound=Reiniciar ronda
btnTournamentInfo=Info del torneo
btnNextRound=Siguiente ronda
btnWonRound=HAS GANADO LA RONDA
btnWonRound=HAS GANADO LA RONDA
btnWonTournament=***¡FELICIDADES! ¡HAS GANADO EL TORNEO!***
btnLoseRound=HAS PERDIDO EN LA RONDA
btnLoseRound=HAS PERDIDO EN LA RONDA
btnQuit=Salir
btnContinue=Continuar
btnRestart=Reiniciar
@@ -1588,8 +1597,8 @@ lblFailedGauntlet=No has podido superar el desafío.
lblLeaveTournamentDraftWarning1=Si te retiras ahora, este torneo se acabará para siempre.\nConservarás las cartas que has sacado, pero no recibirás ningún otro premio.\n\n¿Sigues queriendo dejar el torneo?
lblLeaveTournamentDraftWarning2=¡Te quedan partidas por jugar!\nSi dejas el torneo antes de tiempo, perderás tus potenciales ganancias futuras.\nSeguirás recibiendo ganancias como si te hubieras rendido en tu próxima partida y te quedarás con las cartas que has sacado.\n\n¿Sigues queriendo dejar el torneo?
lblReallyQuit=¿Realmente quieres abandonar?
lblForPlacing=Para colocar
lblHaveBeAward=, has sido premiado
lblForPlacing=Para colocar
lblHaveBeAward=, has sido premiado
lblTournamentReward=Recompensa del Torneo
lblParticipateingTournamentReward=Por participar en el torneo, se le ha premiado con la siguiente carta promocional:
lblCreditsAwarded=Créditos ganados
@@ -1608,22 +1617,22 @@ lblWouldLikeSaveDraft=¿Te gustaría guardar este Draft en el modo de draft norm
lblSaveDraft=Guardar Draft
lblNoAvailableDraftsMessage=¡No tienes ningún set de draft desbloqueado!\nVuelve más tarde cuando hayas desbloqueado más ediciones.
lblNoAvailableDrafts=No hay Drafts disponibles
lblEntryFeeOfDraftTournament=La cuota de inscripción para este torneo de booster draft es
lblEntryFeeOfDraftTournament=La cuota de inscripción para este torneo de booster draft es
lblWouldLikeCreateTournament= créditos.\n¿Te gustaría gastar una ficha y crear este torneo?
lblCreatingDraftTournament=Creación de un Torneo de Booster Draft
lblUnexpectedCreatingDraftTournament=Error inesperado al crear un torneo de draft
lblUnexpectedCreatingDraftTournament=Error inesperado al crear un torneo de draft
lblPleaseReportBug=. Por favor, informe de esto como un error.
lbl1stPlace=1er Puesto:
lbl2ndPlace=2o Puesto:
lbl3rdPlace=3er Puesto:
lbl4thPlace=4o Puesto:
lbl1stPlace=1er Puesto:
lbl2ndPlace=2o Puesto:
lbl3rdPlace=3er Puesto:
lbl4thPlace=4o Puesto:
lblTime= veces
lblCollectPrizes=Recoger Premios
lblCurrentlyInDraft=Actualmente estás en un draft.\nDeberías dejar o terminar ese draft antes de empezar otro.
lblYouNeed=Necesitas
lblYouNeed=Necesitas
lblMoreCredits=créditos más para entrar en este torneo.
lblNotEnoughCredits=No hay suficientes créditos
lblTournamentCosts=Este torneo cuesta
lblTournamentCosts=Este torneo cuesta
lblSureEnterTournament= créditos para entrar.\n¿Está seguro de que desea entrar?
lblEnterDraftTournament=¿Entrar en el Torneo de Draft?
lblLeaveDraftConfirm=Esto terminará con el actual Draft y no podrás volver a unirte a este torneo.\nTus créditos serán reembolsados y el Draft será eliminado.\n\n¿Abandonar de todos modos?
@@ -1683,7 +1692,7 @@ lblWinsForRankIncrease=Ganancias por aumento de rango
lblWinsForMediumAI=Ganancias para la IA media
lblWinsForHardAI=Ganancias para la IA difícil
lblWinsForExpertAI=Ganancias para la IA experta
lblSaveFailed=Error al Guardar -
lblSaveFailed=Error al Guardar -
#QuestSpellShopScreen.java
lblMaximumSellingCredits=El precio máximo de venta es de %d créditos.
lblSellCardsAt=Vender cartas a
@@ -1769,7 +1778,7 @@ lblDoYouWantMoveTargetFromOriToDest=¿Quieres mover {0} de {1} a {2}?
lblPutThatCardFromPlayerOriginToDestination=¿Poner esa carta desde {0} {1} hasta {2}?
lblSearchPlayerZoneConfirm=¿Buscar {0} {1}?
lblCardMatchSearchingTypeInAlternateZones=las cartas coinciden con tu tipo de búsqueda en las zonas alternativas.
lblLookingCardIn=Mirando las cartas en
lblLookingCardIn=Mirando las cartas en
lblDoYouWantPlayCard=¿Quieres jugar {0}?
lblSelectCardFromPlayerZone=Selecciona una carta de {0} {1}
lblSelectUpToNumCardFromPlayerZone=Selecciona hasta {0} cartas de {1} {2}
@@ -1861,7 +1870,7 @@ lblChooseACardLeaveTarget=Elige una carta para dejar en {0} {1}
lblChooseCardsPutIntoZone=Elige la(s) carta(s) a poner en {0}
lblChooseCardPutOnTargetLibraryBottom=Elige la(s) carta(s) a poner en la parte inferior de la biblioteca {0}
lblChooseCardPutOnTargetLibraryTop=Elige la(s) carta(s) a poner en la parte superior de la biblioteca {0}
lblPlayerPickedCardFrom={0} eligió carta(s) de
lblPlayerPickedCardFrom={0} eligió carta(s) de
lblNoValidCards=No hay cartas válidas
#DigUntilEffect.java
lblDoYouWantDigYourLibrary=¿Quieres escarbar en tu biblioteca?
@@ -2281,7 +2290,7 @@ lblDoYouWantCardDealNDamageToYou=¿Quieres que {0} te haga {1} de daño?
lblDrawNCardsConfirm=¿Robar {0} carta(s)?
lblExileConfirm=¿Exiliar {0}?
lblExileNCardsFromYourZone=Exilia {0} carta(s) de tu {1}
lblExileFromWhoseZone=Exilia {0} de quién
lblExileFromWhoseZone=Exilia {0} de quién
lblExileProgressFromZone=Exilia {0}/{1} de {2}
lblToBeExiled=Para ser exiliado
lblExileFromStack=Exiliar de la pila
@@ -2312,7 +2321,7 @@ lblRemoveNTargetCounterFromCardPayCostConfirm=Paga el coste: ¿Quitar el contado
lblRemoveCountersFromAInZoneCard=Quitar contador(es) de una carta en {0}
lblSacrificeCardConfirm=¿Sacrificar {0}?
lblSelectATargetToSacrifice=Selecciona un {0} para sacrificar ({1} pendiente)
lblSelectOneOfCardsToTapAlreadyChosen=Selecciona una de las cartas para girar. Ya elegido:
lblSelectOneOfCardsToTapAlreadyChosen=Selecciona una de las cartas para girar. Ya elegido:
lblSelectACreatureToTap=Selecciona una criatura para girar.
lblEnoughValidCardNotToPayTheCost=No quedan suficientes cartas válidas para girar para pagar el coste.
lblCostPaymentInvalid=Pago del coste no válido
@@ -2570,7 +2579,7 @@ lblRecordAndAssets=Registro | Bienes
lblXWinOfYLost={0} G/{1} P
lblDeleteThisQuest=Eliminar esta aventura
lblRenameThisQuest=Renombrar esta aventura
lblRenameQuestTo=Renombrar aventura como
lblRenameQuestTo=Renombrar aventura como
lblQuestRename=Renombrar aventura
#StartingPoolType.java
lblUnrestricted=Sin restriciones
@@ -2671,7 +2680,7 @@ lblDetectedInvalidHostAddress=Dirección de host inválida ({0}) detectada.
#Player.java
lblChooseACompanion=Elige un compañero
lblChooseAColorFor=Elige un color para {0}
lblRevealFaceDownCards=Mostrar cartas boca abajo de
lblRevealFaceDownCards=Mostrar cartas boca abajo de
lblLearnALesson=Learn a Lesson
#QuestPreferences.java
lblWildOpponentNumberError=Los oponentes salvajes sólo pueden ser de 0 a 3

View File

@@ -18,8 +18,8 @@ lblerrLoadingLayoutFile=Impossibile leggere il file di layout %s. Verrà elimina
lblLoadingQuest=Caricamento avevntura ...
#FScreen.java
#translate lblHomeWithSpaces,lblDeckEditorWithSpaces need keep spaces in text
lblHomeWithSpaces=Home
lblDeckEditorWithSpaces=Gestione dei mazzi
lblHomeWithSpaces=Home
lblDeckEditorWithSpaces=Gestione dei mazzi
lblWorkshop=Officina
lblBacktoHome=Torna a Home
lblCloseEditor=Esci dalla gestione
@@ -361,6 +361,15 @@ lblTotalDamageText=Punti di danno disponibili: Sconosciuto
lblAssignRemainingText=Distribuire i punti di danno rimanenti tra le entità ferite letalmente
lblLethal=Letale
lblAvailableDamagePoints=Punti danno disponibili
#VAssignGenericAmount.java
# The {0} below should be amount label (like "shield" or "damage"), and {1} will be the name of the effect source
lbLAssignAmountForEffect=Assign {0} by {1}
lblLClickAmountMessage=Left click: Assign 1 {0}. (Left Click + Control): Assign maximum {0} points
lblRClickAmountMessage=Right click: Unassign 1 {0}. (Right Click + Control): Unassign all {0} points.
lblTotalAmountText=Available {0} points: Unknown
lblAvailableAmount=Available {0} points
lblMax=Max
lblShield=shield
#KeyboardShortcuts.java
lblSHORTCUT_SHOWSTACK=Incontro: mostra il pannello della pila
lblSHORTCUT_SHOWCOMBAT=Incontro: mostra il pannello di combattimento
@@ -1613,10 +1622,10 @@ lblWouldLikeCreateTournament= monete.\nDesideri usare un token e creare questo t
lblCreatingDraftTournament=Creazione del torneo Booster Draft
lblUnexpectedCreatingDraftTournament=Errore inaspettato durante la creazione del torneo
lblPleaseReportBug=. Per favore segnala il bug.
lbl1stPlace=Primo posto:
lbl2ndPlace=Secondo posto:
lbl3rdPlace=Terzo posto:
lbl4thPlace=Quarto posto:
lbl1stPlace=Primo posto:
lbl2ndPlace=Secondo posto:
lbl3rdPlace=Terzo posto:
lbl4thPlace=Quarto posto:
lblTime= tempo
lblCollectPrizes=Ricevi i premi
lblCurrentlyInDraft=Stai partecipando a un draft.\nDevi terminare o abbandonare quel draft prima di iniziarne un altro.
@@ -1683,10 +1692,10 @@ lblWinsForRankIncrease=Vittorie per aumento di rango
lblWinsForMediumAI=Numero di vittorie per IA livello Apprendista
lblWinsForHardAI=Numero di vittorie per IA livello Esperto
lblWinsForExpertAI=Numero di vittorie per IA livello Maestro
lblSaveFailed=Salvataggio fallito -
lblSaveFailed=Salvataggio fallito -
#QuestSpellShopScreen.java
lblMaximumSellingCredits=Il prezzo massimo di vendita è di %d monete.
lblSellCardsAt=Carte in vendita al
lblSellCardsAt=Carte in vendita al
lblTheirValue=% del loro valore.\n
lblSell=Vendi
lblItem=oggetto
@@ -2274,7 +2283,7 @@ lblHistoriiansWillRecallYourConquestAs=Gli storici ricorderanno la tua Conquista
lblConquestName=Nome della Conquista
#HumanCostDecision.java
lblChooseXValueForCard={0} - Scegli un valore per X
lblSelectOneSameNameCardToDiscardAlreadyChosen=Seleziona una delle carte con lo stesso nome da scartare. Già scelto:
lblSelectOneSameNameCardToDiscardAlreadyChosen=Seleziona una delle carte con lo stesso nome da scartare. Già scelto:
lblSelectOneDifferentNameCardToDiscardAlreadyChosen=Seleziona una delle carte con un nome diverso da scartare. Già scelto:
lblSelectNMoreTargetTypeCardToDiscard=Seleziona altre{0} {1} da scartare.
lblDoYouWantCardDealNDamageToYou=Vuoi che {0} ti infligga {1} danno/i?
@@ -2312,7 +2321,7 @@ lblRemoveNTargetCounterFromCardPayCostConfirm=Paga il costot: Vuoi rimuovere {0}
lblRemoveCountersFromAInZoneCard=Rimuovi segnalini da una carta in {0}
lblSacrificeCardConfirm=Vuoi sacrificare {0}?
lblSelectATargetToSacrifice=Seleziona una carta {0} da scarificare ({1} rimasta/e)
lblSelectOneOfCardsToTapAlreadyChosen=Seleziona una delle carta da tappare. Già scelto:
lblSelectOneOfCardsToTapAlreadyChosen=Seleziona una delle carta da tappare. Già scelto:
lblSelectACreatureToTap=Seleziona una creatura da tappare.
lblEnoughValidCardNotToPayTheCost=Non sono rimaste abbastanza carte valide da tappare per pagare il costo
lblCostPaymentInvalid=Pagamento del costo non valido
@@ -2386,7 +2395,7 @@ lblHandOrLibraryOrGraveyard=mano, grimorio e cimitero
lblHandOrLibraryOrGraveyardOrBattlefield=mano, grimorio, cimitero e campo di battaglia
#LifeToSpare.java
lblLifeToSpare=Vita da vendere
lblWinGameWith=Vinci una partita con
lblWinGameWith=Vinci una partita con
lblMoreThanStartedLifeN={0} punti vita in più di quelli con cui hai iniziato
#ManaFlooded.java
lblManaFlooded=Inondazione di mana

View File

@@ -18,8 +18,8 @@ lblerrLoadingLayoutFile=%s レイアウトファイルを読み取れません
lblLoadingQuest=クエストを読み込んでいます...
#FScreen.java
#translate lblHomeWithSpaces,lblDeckEditorWithSpaces need keep spaces in text
lblHomeWithSpaces=ホーム
lblDeckEditorWithSpaces=デッキエディター
lblHomeWithSpaces=ホーム
lblDeckEditorWithSpaces=デッキエディター
lblWorkshop=ワークショップ
lblBacktoHome=ホームに戻る
lblCloseEditor=エディターを閉じる
@@ -355,12 +355,21 @@ lblReset=リセット
lblAuto=自動
#VAssignDamage.java
lbLAssignDamageDealtBy=%sによって与えられるダメージを割り当てます
lblLClickDamageMessage=左クリック1ダメージを割り当てます。 Ctrl + 左クリック):残りのダメージを致死まで割り当て
lblRClickDamageMessage=右クリック1ダメージの割り当てを解除します。 Ctrl + 右クリック):すべての損傷の割り当てを解除します。
lblLClickDamageMessage=左クリック1ダメージを割り当てます。 Ctrl + 左クリック):残りのダメージを致死まで割り当てます。
lblRClickDamageMessage=右クリック1ダメージの割り当てを解除します。 Ctrl + 右クリック):すべてのダメージの割り当てを解除します。
lblTotalDamageText=利用可能なダメージポイント:不明
lblAssignRemainingText=命傷を負ったエンティティに残りのダメージポイントを分配する
lblAssignRemainingText=死ダメージを負ったオブジェクトに残りのダメージを割り当てます。
lblLethal=リーサル
lblAvailableDamagePoints=利用可能なダメージポイント
#VAssignGenericAmount.java
# The {0} below should be amount label (like "shield" or "damage"), and {1} will be the name of the effect source
lbLAssignAmountForEffect={1}によって効果の{0}を割り当てます
lblLClickAmountMessage=左クリック: 1点{0}を割り当てます。 Ctrl + 左クリック):{0}を最大まで割り当てます。
lblRClickAmountMessage=右クリック1点{0}の割り当てを解除します。 Ctrl + 右クリック):すべての{0}の割り当てを解除します。
lblTotalAmountText=残りの{0}のポイント:不明
lblAvailableAmount=残りの{0}のポイント
lblMax=最大
lblShield=
#KeyboardShortcuts.java
lblSHORTCUT_SHOWSTACK=スタックパネルを表示
lblSHORTCUT_SHOWCOMBAT=戦闘パネルを表示
@@ -1039,7 +1048,7 @@ lblLog=ログ
lblDev=開発者
lblCombatTab=戦闘
lblStack=スタック
lblMustWaitPriority=優先権を待つ必要があります...
lblMustWaitPriority=優先権を待つ必要があります...
#FDeckEditor.java
lblImportFromClipboard=クリップボードからインポート
lblSaveAs=名前を付けて保存...
@@ -1075,7 +1084,7 @@ lblLoadingCardTranslations=カードの翻訳を読み込んでいます...
lblFinishingStartup=起動を終了しています...
lblPreloadExtendedArt=拡張アートのプリロード...
#LobbyScreen.java
lblMore=もっと...
lblMore=もっと...
lblLoadingNewGame=新しいゲームを読み込んでいます...
lblSelectVariants=バリエーションを選択
msgSelectAdeckBeforeReadying=始まる前にデッキを選択してください!
@@ -1624,7 +1633,7 @@ lblCurrentlyInDraft=今はドラフトを参加中です。\n新しいドラフ
lblYouNeed=このトーナメントを参加するにはもっと
lblMoreCredits=のクレジットが必要です。
lblNotEnoughCredits=クレジット不足
lblTournamentCosts=このトーナメントの参加費は
lblTournamentCosts=このトーナメントの参加費は
lblSureEnterTournament=クレジットです。\n参加してもよろしいでしょうか
lblEnterDraftTournament=トーナメントに参加しますか?
lblLeaveDraftConfirm=これによりこのドラフトを終了して、再度参加することは出来ません。\n使ったクレジットは返金されて、そしてドラフトを削除します。\n\nそれでも退場しますか
@@ -2275,8 +2284,8 @@ lblHistoriiansWillRecallYourConquestAs=歴史家たちはあなたの征服紀
lblConquestName=征服紀録名
#HumanCostDecision.java
lblChooseXValueForCard={0} - Xの値を選択
lblSelectOneSameNameCardToDiscardAlreadyChosen=同名カードを 1つ選んで捨てる。既に選択
lblSelectOneDifferentNameCardToDiscardAlreadyChosen=違う名前のカードを 1つ選んで捨てる。既に選択
lblSelectOneSameNameCardToDiscardAlreadyChosen=同名カードを 1つ選んで捨てる。既に選択
lblSelectOneDifferentNameCardToDiscardAlreadyChosen=違う名前のカードを 1つ選んで捨てる。既に選択
lblSelectNMoreTargetTypeCardToDiscard=もっと {0}枚の {1}を選んで捨てる。
lblDoYouWantCardDealNDamageToYou={0}があなたに {1}点のダメージを与えてもいいですか?
lblDrawNCardsConfirm={0}枚のカードを引きますか?

View File

@@ -18,8 +18,8 @@ lblerrLoadingLayoutFile=无法读取你的布局文件:%s。按OK然后删除
lblLoadingQuest=加载冒险之旅...
#FScreen.java
#翻译lblHomeWithSpaces,lblDeckEditorWithSpaces时需要保留翻译文本中的空格
lblHomeWithSpaces=主页
lblDeckEditorWithSpaces=套牌编辑器
lblHomeWithSpaces=主页
lblDeckEditorWithSpaces=套牌编辑器
lblWorkshop=作坊页面
lblBacktoHome=回退到主页
lblCloseEditor=关闭编辑器
@@ -53,7 +53,7 @@ btnClearImageCache=清除图片缓存
btnTokenPreviewer=衍生物预览器
btnCopyToClipboard=复制到剪切板
cbpSelectLanguage=语言
cbpAutoUpdater=自动更新
cbpAutoUpdater=自动更新
nlAutoUpdater=选择用于更新Forge的发布渠道
nlSelectLanguage=选择语言(除了正在进行中的游戏)(需要重新启动)
cbRemoveSmall=删除小生物
@@ -361,6 +361,15 @@ lblTotalDamageText=可用的伤害值:未知
lblAssignRemainingText=对受到致命伤害的对象分配过量的伤害
lblLethal=致死
lblAvailableDamagePoints=可用的伤害值
#VAssignGenericAmount.java
# The {0} below should be amount label (like "shield" or "damage"), and {1} will be the name of the effect source
lbLAssignAmountForEffect=Assign {0} by {1}
lblLClickAmountMessage=Left click: Assign 1 {0}. (Left Click + Control): Assign maximum {0} points
lblRClickAmountMessage=Right click: Unassign 1 {0}. (Right Click + Control): Unassign all {0} points.
lblTotalAmountText=Available {0} points: Unknown
lblAvailableAmount=Available {0} points
lblMax=Max
lblShield=shield
#KeyboardShortcuts.java
lblSHORTCUT_SHOWSTACK=匹配:显示堆叠面板
lblSHORTCUT_SHOWCOMBAT=匹配:显示战斗面板
@@ -416,7 +425,7 @@ lblSelectOpponentDeck=为对手选择套牌
lblGenerateNewDeck=生成新的套牌
lblRandomTheme=随机主题
lblTestDeck=测试套牌
lblLoading=加载中
lblLoading=加载中
#GameType.java
lblSealed=现开
lblDraft=轮抓
@@ -764,7 +773,7 @@ ttWinsperDraftRotation=如果轮抓没有赢这么多场,那么它将被删除
lblRotationType=轮替类型
ttRotationType=如果设置为0旧系列消失如果设置为1则用不同系列替换。
lblWildOpponentNumber=野外对手数量
lblWildOpponentMultiplier=野外对手倍数
lblWildOpponentMultiplier=野外对手倍数
#StatTypeFilter.java
lblclicktotoogle=单击以切换筛选器,右键单机以仅显示
#SItemManagerUtil.java
@@ -1288,7 +1297,7 @@ lblLibrary=牌库
lblGraveyard=坟场
lblTop=
lblBottom=
lblNColorManaFromCard={2}产{0}个{1}法术力
lblNColorManaFromCard={2}产{0}个{1}法术力
lblPayManaFromManaPool=从法术力池支付法术力
lblChooseATargetType=选择一个{0}类型
lblUntap=重置
@@ -1621,10 +1630,10 @@ lbl4thPlace=第四名:
lblTime= 时间
lblCollectPrizes=收藏品
lblCurrentlyInDraft=你现在正在一场轮抓中。\n你应该先离开轮抓或者玩完这场轮抓然后再开始新的轮抓。
lblYouNeed=你还需要
lblYouNeed=你还需要
lblMoreCredits=积分才可以参与这场锦标赛。
lblNotEnoughCredits=积分不足
lblTournamentCosts=这次锦标赛的费用为
lblTournamentCosts=这次锦标赛的费用为
lblSureEnterTournament= 积分。\n你确定要进入吗
lblEnterDraftTournament=进入轮抓锦标赛?
lblLeaveDraftConfirm=这将结束当前轮抓,你将无法再次加入此锦标赛。\n你的积分将被退还轮抓将被删除。\n\n仍然要离开吗

View File

@@ -56,6 +56,7 @@ public enum ProtocolMethod {
setPanelSelection (Mode.SERVER, Void.TYPE, CardView.class),
getAbilityToPlay (Mode.SERVER, SpellAbilityView.class, CardView.class, List/*SpellAbilityView*/.class, ITriggerEvent.class),
assignCombatDamage (Mode.SERVER, Map.class, CardView.class, List/*CardView*/.class, Integer.TYPE, GameEntityView.class, Boolean.TYPE),
divideShield (Mode.SERVER, Map.class, CardView.class, Map.class, Integer.TYPE, Boolean.TYPE, String.class),
message (Mode.SERVER, Void.TYPE, String.class, String.class),
showErrorDialog (Mode.SERVER, Void.TYPE, String.class, String.class),
showConfirmDialog (Mode.SERVER, Boolean.TYPE, String.class, String.class, String.class, String.class, Boolean.TYPE),

View File

@@ -74,7 +74,7 @@ public class NetGuiGame extends AbstractGuiGame {
updateGameView();
send(ProtocolMethod.showPromptMessage, playerView, message);
}
@Override
public void showPromptMessage(final PlayerView playerView, final String message, final CardView card) {
updateGameView();
@@ -206,6 +206,11 @@ public class NetGuiGame extends AbstractGuiGame {
return sendAndWait(ProtocolMethod.assignCombatDamage, attacker, blockers, damage, defender, overrideOrder);
}
@Override
public Map<GameEntityView, Integer> assignGenericAmount(final CardView effectSource, final Map<GameEntityView, Integer> targets, final int amount, final boolean atLeastOne, final String amountLabel) {
return sendAndWait(ProtocolMethod.divideShield, effectSource, targets, amount, atLeastOne, amountLabel);
}
@Override
public void message(final String message, final String title) {
send(ProtocolMethod.message, message, title);

View File

@@ -69,6 +69,7 @@ public interface IGuiGame {
void setPanelSelection(CardView hostCard);
SpellAbilityView getAbilityToPlay(CardView hostCard, List<SpellAbilityView> abilities, ITriggerEvent triggerEvent);
Map<CardView, Integer> assignCombatDamage(CardView attacker, List<CardView> blockers, int damage, GameEntityView defender, boolean overrideOrder);
Map<GameEntityView, Integer> assignGenericAmount(CardView effectSource, Map<GameEntityView, Integer> target, int amount, final boolean atLeastOne, final String amountLabel);
void message(String message);
void message(String message, String title);

View File

@@ -11,6 +11,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -389,6 +390,24 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
}
}
@Override
public Map<GameEntity, Integer> divideShield(Card effectSource, Map<GameEntity, Integer> affected, int shieldAmount) {
final CardView vSource = CardView.get(effectSource);
final Map<GameEntityView, Integer> vAffected = new HashMap<>(affected.size());
for (Map.Entry<GameEntity, Integer> e : affected.entrySet()) {
vAffected.put(GameEntityView.get(e.getKey()), e.getValue());
}
final Map<GameEntityView, Integer> vResult = getGui().assignGenericAmount(vSource, vAffected, shieldAmount, false,
localizer.getMessage("lblShield"));
Map<GameEntity, Integer> result = new HashMap<>(vResult.size());
for (Map.Entry<GameEntity, Integer> e : affected.entrySet()) {
if (vResult.containsKey(GameEntityView.get(e.getKey()))) {
result.put(e.getKey(), vResult.get(GameEntityView.get(e.getKey())));
}
}
return result;
}
@Override
public Integer announceRequirements(final SpellAbility ability, final String announce) {
int max = Integer.MAX_VALUE;