mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
Implement missing cards with dividable shields for damage replacement effects
This commit is contained in:
@@ -131,6 +131,12 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
return ComputerUtilCombat.distributeAIDamage(attacker, blockers, damageDealt, defender, overrideOrder);
|
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
|
@Override
|
||||||
public Integer announceRequirements(SpellAbility ability, String announce) {
|
public Integer announceRequirements(SpellAbility ability, String announce) {
|
||||||
// For now, these "announcements" are made within the AI classes of the appropriate SA effects
|
// For now, these "announcements" are made within the AI classes of the appropriate SA effects
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ public enum AbilityKey {
|
|||||||
DefendingPlayer("DefendingPlayer"),
|
DefendingPlayer("DefendingPlayer"),
|
||||||
Destination("Destination"),
|
Destination("Destination"),
|
||||||
Devoured("Devoured"),
|
Devoured("Devoured"),
|
||||||
|
DividedShieldAmount("DividedShieldAmount"),
|
||||||
EchoPaid("EchoPaid"),
|
EchoPaid("EchoPaid"),
|
||||||
EffectOnly("EffectOnly"),
|
EffectOnly("EffectOnly"),
|
||||||
Exploited("Exploited"),
|
Exploited("Exploited"),
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ public class ReplaceDamageEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
if (prevent > 0) {
|
if (prevent > 0) {
|
||||||
int n = Math.min(dmg, prevent);
|
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;
|
dmg -= n;
|
||||||
prevent -= n;
|
prevent -= n;
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
if (prevent > 0 && list.size() > 0 && list.get(0) instanceof GameEntity) {
|
if (prevent > 0 && list.size() > 0 && list.get(0) instanceof GameEntity) {
|
||||||
int n = Math.min(dmg, prevent);
|
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;
|
dmg -= n;
|
||||||
prevent -= n;
|
prevent -= n;
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ public abstract class PlayerController {
|
|||||||
public abstract List<PaperCard> chooseCardsYouWonToAddToDeck(List<PaperCard> losses);
|
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<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 Integer announceRequirements(SpellAbility ability, String announce);
|
||||||
public abstract CardCollectionView choosePermanentsToSacrifice(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);
|
public abstract CardCollectionView choosePermanentsToSacrifice(SpellAbility sa, int min, int max, CardCollectionView validTargets, String message);
|
||||||
|
|||||||
@@ -688,6 +688,7 @@ public class ReplacementHandler {
|
|||||||
ApiType apiType = null;
|
ApiType apiType = null;
|
||||||
SpellAbility bufferedSA = effectSA;
|
SpellAbility bufferedSA = effectSA;
|
||||||
boolean needRestoreSubSA = false;
|
boolean needRestoreSubSA = false;
|
||||||
|
boolean needDivideShield = false;
|
||||||
boolean needChooseSource = false;
|
boolean needChooseSource = false;
|
||||||
int shieldAmount = 0;
|
int shieldAmount = 0;
|
||||||
if (effectSA != null) {
|
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();
|
Map<String, String> mapParams = chosenRE.getMapParams();
|
||||||
if ((mapParams.containsKey("PreventionEffect") && mapParams.get("PreventionEffect").equals("NextN"))
|
if ((mapParams.containsKey("PreventionEffect") && mapParams.get("PreventionEffect").equals("NextN"))
|
||||||
|| apiType == ApiType.ReplaceSplitDamage) {
|
|| apiType == ApiType.ReplaceSplitDamage) {
|
||||||
@@ -711,17 +713,50 @@ public class ReplacementHandler {
|
|||||||
shieldAmount = AbilityUtils.calculateAmount(effectSA.getHostCard(), effectSA.getParamOrDefault("VarName", "1"), effectSA);
|
shieldAmount = AbilityUtils.calculateAmount(effectSA.getHostCard(), effectSA.getParamOrDefault("VarName", "1"), effectSA);
|
||||||
}
|
}
|
||||||
int damageAmount = 0;
|
int damageAmount = 0;
|
||||||
|
boolean hasMultipleSource = false;
|
||||||
|
boolean hasMultipleTarget = false;
|
||||||
|
Card firstSource = null;
|
||||||
|
GameEntity firstTarget = null;
|
||||||
for (Map<AbilityKey, Object> runParams : runParamList) {
|
for (Map<AbilityKey, Object> runParams : runParamList) {
|
||||||
// Only count damage that can be prevented
|
// Only count damage that can be prevented
|
||||||
if (apiType == ApiType.ReplaceDamage && Boolean.TRUE.equals(runParams.get(AbilityKey.NoPreventDamage))) continue;
|
if (apiType == ApiType.ReplaceDamage && Boolean.TRUE.equals(runParams.get(AbilityKey.NoPreventDamage))) continue;
|
||||||
damageAmount += (int) runParams.get(AbilityKey.DamageAmount);
|
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 (damageAmount > shieldAmount) {
|
if (firstTarget == null) {
|
||||||
|
firstTarget = (GameEntity) runParams.get(AbilityKey.Affected);
|
||||||
|
} else if (!firstTarget.equals(runParams.get(AbilityKey.Affected))) {
|
||||||
|
hasMultipleTarget = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (damageAmount > shieldAmount && runParamList.size() > 1) {
|
||||||
|
if (hasMultipleSource)
|
||||||
needChooseSource = true;
|
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
|
// CR 615.7
|
||||||
// If damage would be dealt to the shielded permanent or player by two or more applicable sources at the same time,
|
// 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.
|
// 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()) {
|
while (shieldAmount > 0 && !sourcesToChooseFrom.isEmpty()) {
|
||||||
Card source = decider.getController().chooseSingleEntityForEffect(sourcesToChooseFrom, effectSA, choiceTitle, null);
|
Card source = decider.getController().chooseSingleEntityForEffect(sourcesToChooseFrom, effectSA, choiceTitle, null);
|
||||||
sourcesToChooseFrom.remove(source);
|
sourcesToChooseFrom.remove(source);
|
||||||
Map<AbilityKey, Object> runParams = null;
|
Iterator<Map<AbilityKey, Object>> itr = runParamList.iterator();
|
||||||
for (Map<AbilityKey, Object> rp : runParamList) {
|
while (itr.hasNext()) {
|
||||||
if (source.equals(rp.get(AbilityKey.DamageSource))) {
|
Map<AbilityKey, Object> runParams = itr.next();
|
||||||
runParams = rp;
|
if (source.equals(runParams.get(AbilityKey.DamageSource))) {
|
||||||
break;
|
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 {
|
||||||
runParamList.remove(runParams);
|
|
||||||
shieldAmount -= (int) runParams.get(AbilityKey.DamageAmount);
|
shieldAmount -= (int) runParams.get(AbilityKey.DamageAmount);
|
||||||
|
}
|
||||||
if (!runParams.containsKey(AbilityKey.ReplacementResultMap)) {
|
if (!runParams.containsKey(AbilityKey.ReplacementResultMap)) {
|
||||||
Map<ReplacementEffect, ReplacementResult> resultMap = new HashMap<>();
|
Map<ReplacementEffect, ReplacementResult> resultMap = new HashMap<>();
|
||||||
runParams.put(AbilityKey.ReplacementResultMap, resultMap);
|
runParams.put(AbilityKey.ReplacementResultMap, resultMap);
|
||||||
}
|
}
|
||||||
runSingleReplaceDamageEffect(chosenRE, runParams, replaceCandidateMap, executedDamageMap, decider, damageMap, preventMap);
|
runSingleReplaceDamageEffect(chosenRE, runParams, replaceCandidateMap, executedDamageMap, decider, damageMap, preventMap);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (Map<AbilityKey, Object> runParams : runParamList) {
|
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)) {
|
if (!runParams.containsKey(AbilityKey.ReplacementResultMap)) {
|
||||||
Map<ReplacementEffect, ReplacementResult> resultMap = new HashMap<>();
|
Map<ReplacementEffect, ReplacementResult> resultMap = new HashMap<>();
|
||||||
runParams.put(AbilityKey.ReplacementResultMap, resultMap);
|
runParams.put(AbilityKey.ReplacementResultMap, resultMap);
|
||||||
|
|||||||
@@ -1028,6 +1028,24 @@ public final class CMatchUI
|
|||||||
return result.get();
|
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
|
@Override
|
||||||
public void openView(final TrackableCollection<PlayerView> myPlayers) {
|
public void openView(final TrackableCollection<PlayerView> myPlayers) {
|
||||||
final GameView gameView = getGameView();
|
final GameView gameView = getGameView();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -146,6 +146,12 @@ public class PlayerControllerForTests extends PlayerController {
|
|||||||
throw new IllegalStateException("Erring on the side of caution here...");
|
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
|
@Override
|
||||||
public Integer announceRequirements(SpellAbility ability, String announce) {
|
public Integer announceRequirements(SpellAbility ability, String announce) {
|
||||||
throw new IllegalStateException("Erring on the side of caution here...");
|
throw new IllegalStateException("Erring on the side of caution here...");
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ import forge.model.FModel;
|
|||||||
import forge.player.PlayerZoneUpdate;
|
import forge.player.PlayerZoneUpdate;
|
||||||
import forge.player.PlayerZoneUpdates;
|
import forge.player.PlayerZoneUpdates;
|
||||||
import forge.screens.match.views.VAssignCombatDamage;
|
import forge.screens.match.views.VAssignCombatDamage;
|
||||||
|
import forge.screens.match.views.VAssignGenericAmount;
|
||||||
import forge.screens.match.views.VPhaseIndicator;
|
import forge.screens.match.views.VPhaseIndicator;
|
||||||
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;
|
||||||
@@ -395,6 +396,18 @@ public class MatchController extends AbstractGuiGame {
|
|||||||
}.invokeAndWait();
|
}.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
|
@Override
|
||||||
public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) {
|
public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) {
|
||||||
for (final PlayerView p : manaPoolUpdate) {
|
for (final PlayerView p : manaPoolUpdate) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
forge-gui/res/cardsfolder/d/divine_deflection.txt
Normal file
13
forge-gui/res/cardsfolder/d/divine_deflection.txt
Normal 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.
|
||||||
11
forge-gui/res/cardsfolder/h/harms_way.txt
Normal file
11
forge-gui/res/cardsfolder/h/harms_way.txt
Normal 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.
|
||||||
15
forge-gui/res/cardsfolder/r/refraction_trap.txt
Normal file
15
forge-gui/res/cardsfolder/r/refraction_trap.txt
Normal 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.
|
||||||
16
forge-gui/res/cardsfolder/s/shining_shoal.txt
Normal file
16
forge-gui/res/cardsfolder/s/shining_shoal.txt
Normal 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.
|
||||||
@@ -361,6 +361,15 @@ lblTotalDamageText=Gesamtschaden: Unbekannt
|
|||||||
lblAssignRemainingText=Verteile verbleibenden Schaden unter tödlich Verwundeten
|
lblAssignRemainingText=Verteile verbleibenden Schaden unter tödlich Verwundeten
|
||||||
lblLethal=Tödlich
|
lblLethal=Tödlich
|
||||||
lblAvailableDamagePoints=Verfügbare Schadenspunkte
|
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
|
#KeyboardShortcuts.java
|
||||||
lblSHORTCUT_SHOWSTACK=Duell: Zeige Stapelfenster
|
lblSHORTCUT_SHOWSTACK=Duell: Zeige Stapelfenster
|
||||||
lblSHORTCUT_SHOWCOMBAT=Duell: Zeige Kampffenster
|
lblSHORTCUT_SHOWCOMBAT=Duell: Zeige Kampffenster
|
||||||
|
|||||||
@@ -361,6 +361,15 @@ lblTotalDamageText=Available damage points: Unknown
|
|||||||
lblAssignRemainingText=Distribute the remaining damage points among lethally wounded entities
|
lblAssignRemainingText=Distribute the remaining damage points among lethally wounded entities
|
||||||
lblLethal=Lethal
|
lblLethal=Lethal
|
||||||
lblAvailableDamagePoints=Available damage points
|
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
|
#KeyboardShortcuts.java
|
||||||
lblSHORTCUT_SHOWSTACK=Match: show stack panel
|
lblSHORTCUT_SHOWSTACK=Match: show stack panel
|
||||||
lblSHORTCUT_SHOWCOMBAT=Match: show combat panel
|
lblSHORTCUT_SHOWCOMBAT=Match: show combat panel
|
||||||
|
|||||||
@@ -361,6 +361,15 @@ lblTotalDamageText=Puntos de daño disponibles: Desconocido
|
|||||||
lblAssignRemainingText=Distribuye los puntos de daño restantes entre las entidades letalmente heridas.
|
lblAssignRemainingText=Distribuye los puntos de daño restantes entre las entidades letalmente heridas.
|
||||||
lblLethal=Letal
|
lblLethal=Letal
|
||||||
lblAvailableDamagePoints=Puntos de daño disponibles
|
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
|
#KeyboardShortcuts.java
|
||||||
lblSHORTCUT_SHOWSTACK=Partida: mostrar panel de pila
|
lblSHORTCUT_SHOWSTACK=Partida: mostrar panel de pila
|
||||||
lblSHORTCUT_SHOWCOMBAT=Partida: mostrar panel de combate
|
lblSHORTCUT_SHOWCOMBAT=Partida: mostrar panel de combate
|
||||||
|
|||||||
@@ -361,6 +361,15 @@ lblTotalDamageText=Punti di danno disponibili: Sconosciuto
|
|||||||
lblAssignRemainingText=Distribuire i punti di danno rimanenti tra le entità ferite letalmente
|
lblAssignRemainingText=Distribuire i punti di danno rimanenti tra le entità ferite letalmente
|
||||||
lblLethal=Letale
|
lblLethal=Letale
|
||||||
lblAvailableDamagePoints=Punti danno disponibili
|
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
|
#KeyboardShortcuts.java
|
||||||
lblSHORTCUT_SHOWSTACK=Incontro: mostra il pannello della pila
|
lblSHORTCUT_SHOWSTACK=Incontro: mostra il pannello della pila
|
||||||
lblSHORTCUT_SHOWCOMBAT=Incontro: mostra il pannello di combattimento
|
lblSHORTCUT_SHOWCOMBAT=Incontro: mostra il pannello di combattimento
|
||||||
|
|||||||
@@ -355,12 +355,21 @@ lblReset=リセット
|
|||||||
lblAuto=自動
|
lblAuto=自動
|
||||||
#VAssignDamage.java
|
#VAssignDamage.java
|
||||||
lbLAssignDamageDealtBy=%sによって与えられるダメージを割り当てます
|
lbLAssignDamageDealtBy=%sによって与えられるダメージを割り当てます
|
||||||
lblLClickDamageMessage=左クリック:1ダメージを割り当てます。 (Ctrl + 左クリック):残りのダメージを致死まで割り当てる
|
lblLClickDamageMessage=左クリック:1点ダメージを割り当てます。 (Ctrl + 左クリック):残りのダメージを致死まで割り当てます。
|
||||||
lblRClickDamageMessage=右クリック:1ダメージの割り当てを解除します。 (Ctrl + 右クリック):すべての損傷の割り当てを解除します。
|
lblRClickDamageMessage=右クリック:1点ダメージの割り当てを解除します。 (Ctrl + 右クリック):すべてのダメージの割り当てを解除します。
|
||||||
lblTotalDamageText=利用可能なダメージポイント:不明
|
lblTotalDamageText=利用可能なダメージポイント:不明
|
||||||
lblAssignRemainingText=致命傷を負ったエンティティに残りのダメージポイントを分配する
|
lblAssignRemainingText=致死ダメージを負ったオブジェクトに残りのダメージを割り当てます。
|
||||||
lblLethal=リーサル
|
lblLethal=リーサル
|
||||||
lblAvailableDamagePoints=利用可能なダメージポイント
|
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
|
#KeyboardShortcuts.java
|
||||||
lblSHORTCUT_SHOWSTACK=スタックパネルを表示
|
lblSHORTCUT_SHOWSTACK=スタックパネルを表示
|
||||||
lblSHORTCUT_SHOWCOMBAT=戦闘パネルを表示
|
lblSHORTCUT_SHOWCOMBAT=戦闘パネルを表示
|
||||||
|
|||||||
@@ -361,6 +361,15 @@ lblTotalDamageText=可用的伤害值:未知
|
|||||||
lblAssignRemainingText=对受到致命伤害的对象分配过量的伤害
|
lblAssignRemainingText=对受到致命伤害的对象分配过量的伤害
|
||||||
lblLethal=致死
|
lblLethal=致死
|
||||||
lblAvailableDamagePoints=可用的伤害值
|
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
|
#KeyboardShortcuts.java
|
||||||
lblSHORTCUT_SHOWSTACK=匹配:显示堆叠面板
|
lblSHORTCUT_SHOWSTACK=匹配:显示堆叠面板
|
||||||
lblSHORTCUT_SHOWCOMBAT=匹配:显示战斗面板
|
lblSHORTCUT_SHOWCOMBAT=匹配:显示战斗面板
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public enum ProtocolMethod {
|
|||||||
setPanelSelection (Mode.SERVER, Void.TYPE, CardView.class),
|
setPanelSelection (Mode.SERVER, Void.TYPE, CardView.class),
|
||||||
getAbilityToPlay (Mode.SERVER, SpellAbilityView.class, CardView.class, List/*SpellAbilityView*/.class, ITriggerEvent.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),
|
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),
|
message (Mode.SERVER, Void.TYPE, String.class, String.class),
|
||||||
showErrorDialog (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),
|
showConfirmDialog (Mode.SERVER, Boolean.TYPE, String.class, String.class, String.class, String.class, Boolean.TYPE),
|
||||||
|
|||||||
@@ -206,6 +206,11 @@ public class NetGuiGame extends AbstractGuiGame {
|
|||||||
return sendAndWait(ProtocolMethod.assignCombatDamage, attacker, blockers, damage, defender, overrideOrder);
|
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
|
@Override
|
||||||
public void message(final String message, final String title) {
|
public void message(final String message, final String title) {
|
||||||
send(ProtocolMethod.message, message, title);
|
send(ProtocolMethod.message, message, title);
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ public interface IGuiGame {
|
|||||||
void setPanelSelection(CardView hostCard);
|
void setPanelSelection(CardView hostCard);
|
||||||
SpellAbilityView getAbilityToPlay(CardView hostCard, List<SpellAbilityView> abilities, ITriggerEvent triggerEvent);
|
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<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);
|
||||||
void message(String message, String title);
|
void message(String message, String title);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
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
|
@Override
|
||||||
public Integer announceRequirements(final SpellAbility ability, final String announce) {
|
public Integer announceRequirements(final SpellAbility ability, final String announce) {
|
||||||
int max = Integer.MAX_VALUE;
|
int max = Integer.MAX_VALUE;
|
||||||
|
|||||||
Reference in New Issue
Block a user