Merge branch 'master' of git.cardforge.org:core-developers/forge into agetian-master

This commit is contained in:
Michael Kamensky
2021-06-05 17:34:06 +03:00
1889 changed files with 19680 additions and 14391 deletions

View File

@@ -68,6 +68,7 @@ public class Forge implements ApplicationListener {
private static boolean isloadingaMatch = false;
public static boolean showFPS = false;
public static boolean altPlayerLayout = false;
public static boolean altZoneTabs = false;
public static String enableUIMask = "Crop";
public static boolean enablePreloadExtendedArt = false;
public static boolean isTabletDevice = false;
@@ -88,7 +89,7 @@ public class Forge implements ApplicationListener {
if (GuiBase.getInterface() == null) {
clipboard = clipboard0;
deviceAdapter = deviceAdapter0;
GuiBase.setUsingAppDirectory(assetDir0.contains("forge.app"));
GuiBase.setUsingAppDirectory(assetDir0.contains("forge.app")); //obb directory on android uses the package name as entrypoint
GuiBase.setInterface(new GuiMobile(assetDir0));
GuiBase.enablePropertyConfig(value);
isPortraitMode = androidOrientation;
@@ -136,6 +137,7 @@ public class Forge implements ApplicationListener {
textureFiltering = prefs.getPrefBoolean(FPref.UI_LIBGDX_TEXTURE_FILTERING);
showFPS = prefs.getPrefBoolean(FPref.UI_SHOW_FPS);
altPlayerLayout = prefs.getPrefBoolean(FPref.UI_ALT_PLAYERINFOLAYOUT);
altZoneTabs = prefs.getPrefBoolean(FPref.UI_ALT_PLAYERZONETABS);
enableUIMask = prefs.getPref(FPref.UI_ENABLE_BORDER_MASKING);
if (prefs.getPref(FPref.UI_ENABLE_BORDER_MASKING).equals("true")) //override old settings if not updated
enableUIMask = "Full";
@@ -149,10 +151,11 @@ public class Forge implements ApplicationListener {
if (autoCache) {
//increase cacheSize for devices with RAM more than 5GB, default is 400. Some phones have more than 10GB RAM (Mi 10, OnePlus 8, S20, etc..)
if (totalDeviceRAM>5000) //devices with more than 10GB RAM will have 1000 Cache size, 700 Cache size for morethan 5GB RAM
cacheSize = totalDeviceRAM>10000 ? 1000: 700;
if (totalDeviceRAM>5000) //devices with more than 10GB RAM will have 800 Cache size, 600 Cache size for morethan 5GB RAM
cacheSize = totalDeviceRAM>10000 ? 800: 600;
}
//init cache
ImageCache.initCache(cacheSize);
final Localizer localizer = Localizer.getInstance();
//load model on background thread (using progress bar to report progress)
@@ -471,7 +474,9 @@ public class Forge implements ApplicationListener {
}
catch (Exception ex) {
graphics.end();
BugReporter.reportException(ex);
//check if sentry is enabled, if not it will call the gui interface but here we end the graphics so we only send it via sentry..
if (BugReporter.isSentryEnabled())
BugReporter.reportException(ex);
} finally {
if(dispose)
ImageCache.disposeTexture();
@@ -523,7 +528,9 @@ public class Forge implements ApplicationListener {
}
catch (Exception ex) {
graphics.end();
BugReporter.reportException(ex);
//check if sentry is enabled, if not it will call the gui interface but here we end the graphics so we only send it via sentry..
if (BugReporter.isSentryEnabled())
BugReporter.reportException(ex);
}
if (showFPS)
frameRate.render();
@@ -543,7 +550,9 @@ public class Forge implements ApplicationListener {
}
catch (Exception ex) {
graphics.end();
BugReporter.reportException(ex);
//check if sentry is enabled, if not it will call the gui interface but here we end the graphics so we only send it via sentry..
if (BugReporter.isSentryEnabled())
BugReporter.reportException(ex);
}
}

View File

@@ -672,9 +672,12 @@ public class Graphics {
batch.draw(image, adjustX(x), adjustY(y, h), w, h);
}
public void drawImage(TextureRegion image, float x, float y, float w, float h) {
batch.draw(image, adjustX(x), adjustY(y, h), w, h);
if (image != null)
batch.draw(image, adjustX(x), adjustY(y, h), w, h);
}
public void drawImage(TextureRegion image, TextureRegion glowImageReference, float x, float y, float w, float h, Color glowColor, boolean selected) {
if (image == null || glowImageReference == null)
return;
//1st image is the image on top of the shader, 2nd image is for the outline reference for the shader glow...
// if the 1st image don't have transparency in the middle (only on the sides, top and bottom, use the 1st image as outline reference...
if (!selected) {
@@ -698,6 +701,8 @@ public class Graphics {
}
}
public void drawDeckBox(FImage cardArt, float scale, TextureRegion image, TextureRegion glowImageReference, float x, float y, float w, float h, Color glowColor, boolean selected) {
if (image == null || glowImageReference == null)
return;
float yBox = y-(h*0.25f);
if (!selected) {
cardArt.draw(this,x+((w-w*scale)/2), y+((h-h*scale)/3f), w*scale, h*scale/1.85f);
@@ -788,6 +793,8 @@ public class Graphics {
drawText(text, font, skinColor.getColor(), x, y, w, h, wrap, horzAlignment, centerVertically);
}
public void drawText(String text, FSkinFont font, Color color, float x, float y, float w, float h, boolean wrap, int horzAlignment, boolean centerVertically) {
if (text == null)
return;
if (alphaComposite < 1) {
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
}

View File

@@ -15,6 +15,7 @@ import forge.Forge;
import forge.assets.FSkinImage.SourceFile;
import forge.card.CardFaceSymbols;
import forge.gui.FThreads;
import forge.gui.GuiBase;
import forge.localinstance.properties.ForgeConstants;
import forge.localinstance.properties.ForgePreferences;
import forge.localinstance.properties.ForgePreferences.FPref;
@@ -240,16 +241,24 @@ public class FSkin {
//hdbuttons
if (f11.exists()) {
Texture t = new Texture(f11, true);
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
textures.put(f11.path(), t);
Forge.hdbuttons = true;
if (GuiBase.isAndroid() && Forge.totalDeviceRAM <5000) {
Forge.hdbuttons = false;
} else {
Texture t = new Texture(f11, true);
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
textures.put(f11.path(), t);
Forge.hdbuttons = true;
}
} else { Forge.hdbuttons = false; } //how to refresh buttons when a theme don't have hd buttons?
if (f12.exists()) {
Texture t = new Texture(f12, true);
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
textures.put(f12.path(), t);
Forge.hdstart = true;
if (GuiBase.isAndroid() && Forge.totalDeviceRAM <5000) {
Forge.hdstart = false;
} else {
Texture t = new Texture(f12, true);
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
textures.put(f12.path(), t);
Forge.hdstart = true;
}
} else { Forge.hdstart = false; }
//update colors
for (final FSkinColor.Colors c : FSkinColor.Colors.values()) {
@@ -258,7 +267,16 @@ public class FSkin {
//load images
for (FSkinImage image : FSkinImage.values()) {
image.load(textures, preferredIcons);
if (GuiBase.isAndroid()) {
if (Forge.totalDeviceRAM>5000)
image.load(textures, preferredIcons);
else if (image.toString().equals("HDMULTI"))
image.load(textures, preferredIcons);
else if (!image.toString().startsWith("HD"))
image.load(textures, preferredIcons);
} else {
image.load(textures, preferredIcons);
}
}
for (FSkinTexture texture : FSkinTexture.values()) {
if (texture != FSkinTexture.BG_TEXTURE) {

View File

@@ -70,21 +70,25 @@ public class ImageCache {
// short prefixes to save memory
private static final Set<String> missingIconKeys = new HashSet<>();
private static final LoadingCache<String, Texture> cache = CacheBuilder.newBuilder()
.maximumSize(Forge.cacheSize)
.expireAfterAccess(15, TimeUnit.MINUTES)
.removalListener(new RemovalListener<String, Texture>() {
@Override
public void onRemoval(RemovalNotification<String, Texture> removalNotification) {
if (removalNotification.wasEvicted()) {
if (removalNotification.getValue() != ImageCache.defaultImage)
removalNotification.getValue().dispose();
private static LoadingCache<String, Texture> cache;
public static void initCache(int capacity) {
cache = CacheBuilder.newBuilder()
.maximumSize(capacity)
.expireAfterAccess(15, TimeUnit.MINUTES)
.removalListener(new RemovalListener<String, Texture>() {
@Override
public void onRemoval(RemovalNotification<String, Texture> removalNotification) {
if (removalNotification.wasEvicted()) {
if (removalNotification.getValue() != ImageCache.defaultImage)
removalNotification.getValue().dispose();
CardRenderer.clearcardArtCache();
CardRenderer.clearcardArtCache();
}
}
}
})
.build(new ImageLoader());
})
.build(new ImageLoader());
System.out.println("Card Texture Cache Size: "+capacity);
}
private static final LoadingCache<String, Texture> otherCache = CacheBuilder.newBuilder().build(new OtherImageLoader());
public static final Texture defaultImage;
public static FImage BlackBorder = FSkinImage.IMG_BORDER_BLACK;

View File

@@ -481,10 +481,8 @@ public class CardRenderer {
drawFoilEffect(g, card, x, y, w, h, false);
}
} else {
if (!Forge.enableUIMask.equals("Off")) //render this if mask is still loading
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
else //draw cards without textures as just a black rectangle
g.fillRect(Color.BLACK, x, y, w, h);
//if card has invalid or no texture due to sudden changes in ImageCache, draw CardImageRenderer instead and wait for it to refresh automatically
CardImageRenderer.drawCardImage(g, CardView.getCardForUi(pc), false, x, y, w, h, pos);
}
}
public static void drawCard(Graphics g, CardView card, float x, float y, float w, float h, CardStackPosition pos, boolean rotate) {
@@ -545,10 +543,8 @@ public class CardRenderer {
}
drawFoilEffect(g, card, x, y, w, h, false);
} else {
if (!Forge.enableUIMask.equals("Off")) //render this if mask is still loading
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos);
else //draw cards without textures as just a black rectangle
g.fillRect(Color.BLACK, x, y, w, h);
//if card has invalid or no texture due to sudden changes in ImageCache, draw CardImageRenderer instead and wait for it to refresh automatically
CardImageRenderer.drawCardImage(g, card, false, x, y, w, h, pos);
}
}

View File

@@ -189,6 +189,7 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
private static DeckEditorPage[] getPages(EditorType editorType) {
final Localizer localizer = Localizer.getInstance();
boolean isLandscape = Forge.isLandscapeMode();
switch (editorType) {
default:
case Constructed:
@@ -212,18 +213,28 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
case Commander:
case TinyLeaders:
case Brawl:
return new DeckEditorPage[] {
return isLandscape ? new DeckEditorPage[] {
new CatalogPage(ItemManagerConfig.CARD_CATALOG),
new DeckSectionPage(DeckSection.Commander, ItemManagerConfig.COMMANDER_SECTION),
new DeckSectionPage(DeckSection.Main),
new DeckSectionPage(DeckSection.Sideboard)
} : new DeckEditorPage[] {
new CatalogPage(ItemManagerConfig.CARD_CATALOG),
new DeckSectionPage(DeckSection.Main),
new DeckSectionPage(DeckSection.Commander, ItemManagerConfig.COMMANDER_SECTION),
new DeckSectionPage(DeckSection.Sideboard)
};
case Oathbreaker:
return new DeckEditorPage[] {
return isLandscape ? new DeckEditorPage[] {
new CatalogPage(ItemManagerConfig.CARD_CATALOG),
new DeckSectionPage(DeckSection.Commander, ItemManagerConfig.OATHBREAKER_SECTION, localizer.getMessage("lblOathbreaker"), FSkinImage.COMMANDER),
new DeckSectionPage(DeckSection.Main),
new DeckSectionPage(DeckSection.Sideboard)
} : new DeckEditorPage[] {
new CatalogPage(ItemManagerConfig.CARD_CATALOG),
new DeckSectionPage(DeckSection.Main),
new DeckSectionPage(DeckSection.Commander, ItemManagerConfig.OATHBREAKER_SECTION, localizer.getMessage("lblOathbreaker"), FSkinImage.COMMANDER),
new DeckSectionPage(DeckSection.Sideboard)
};
case Archenemy:
return new DeckEditorPage[] {
@@ -242,16 +253,24 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
new DeckSectionPage(DeckSection.Sideboard, ItemManagerConfig.QUEST_DECK_EDITOR)
};
case QuestCommander:
return new DeckEditorPage[] {
return isLandscape ? new DeckEditorPage[] {
new CatalogPage(ItemManagerConfig.QUEST_EDITOR_POOL, localizer.getMessage("lblInventory"), FSkinImage.QUEST_BOX),
new DeckSectionPage(DeckSection.Commander, ItemManagerConfig.COMMANDER_SECTION),
new DeckSectionPage(DeckSection.Main, ItemManagerConfig.QUEST_DECK_EDITOR)
} : new DeckEditorPage[] {
new CatalogPage(ItemManagerConfig.QUEST_EDITOR_POOL, localizer.getMessage("lblInventory"), FSkinImage.QUEST_BOX),
new DeckSectionPage(DeckSection.Main, ItemManagerConfig.QUEST_DECK_EDITOR),
new DeckSectionPage(DeckSection.Commander, ItemManagerConfig.COMMANDER_SECTION)
};
case PlanarConquest:
return new DeckEditorPage[] {
return isLandscape ? new DeckEditorPage[] {
new CatalogPage(ItemManagerConfig.CONQUEST_COLLECTION, localizer.getMessage("lblCollection"), FSkinImage.SPELLBOOK),
new DeckSectionPage(DeckSection.Commander, ItemManagerConfig.COMMANDER_SECTION),
new DeckSectionPage(DeckSection.Main, ItemManagerConfig.CONQUEST_DECK_EDITOR, localizer.getMessage("lblDeck"), Forge.hdbuttons ? FSkinImage.HDLIBRARY : FSkinImage.DECKLIST)
} : new DeckEditorPage[] {
new CatalogPage(ItemManagerConfig.CONQUEST_COLLECTION, localizer.getMessage("lblCollection"), FSkinImage.SPELLBOOK),
new DeckSectionPage(DeckSection.Main, ItemManagerConfig.CONQUEST_DECK_EDITOR, localizer.getMessage("lblDeck"), Forge.hdbuttons ? FSkinImage.HDLIBRARY : FSkinImage.DECKLIST),
new DeckSectionPage(DeckSection.Commander, ItemManagerConfig.COMMANDER_SECTION)
};
}
}

View File

@@ -24,13 +24,13 @@ public class NewGameMenu extends FPopupMenu {
final static Localizer localizer = Localizer.getInstance();
public enum NewGameScreen {
Constructed(localizer.getMessage("lblConstructed"), FSkinImage.MENU_CONSTRUCTED, ConstructedScreen.class),
BoosterDraft(localizer.getMessage("lblBoosterDraft"), FSkinImage.MENU_DRAFT, NewDraftScreen.class),
SealedDeck(localizer.getMessage("lblSealedDeck"), FSkinImage.MENU_SEALED, NewSealedScreen.class),
QuestMode(localizer.getMessage("lblQuestMode"), FSkinImage.QUEST_ZEP, NewQuestScreen.class),
PuzzleMode(localizer.getMessage("lblPuzzleMode"), FSkinImage.MENU_PUZZLE, PuzzleScreen.class),
PlanarConquest(localizer.getMessage("lblPlanarConquest"), FSkinImage.MENU_GALAXY, NewConquestScreen.class),
Gauntlet(localizer.getMessage("lblGauntlet"), FSkinImage.MENU_GAUNTLET, NewGauntletScreen.class);
Constructed(localizer.getMessageorUseDefault("lblConstructed", "Constructed"), FSkinImage.MENU_CONSTRUCTED, ConstructedScreen.class),
BoosterDraft(localizer.getMessageorUseDefault("lblBoosterDraft", "Booster Draft"), FSkinImage.MENU_DRAFT, NewDraftScreen.class),
SealedDeck(localizer.getMessageorUseDefault("lblSealedDeck", "Sealed Deck"), FSkinImage.MENU_SEALED, NewSealedScreen.class),
QuestMode(localizer.getMessageorUseDefault("lblQuestMode", "Quest Mode"), FSkinImage.QUEST_ZEP, NewQuestScreen.class),
PuzzleMode(localizer.getMessageorUseDefault("lblPuzzleMode", "Puzzle Mode"), FSkinImage.MENU_PUZZLE, PuzzleScreen.class),
PlanarConquest(localizer.getMessageorUseDefault("lblPlanarConquest", "Planar Conquest"), FSkinImage.MENU_GALAXY, NewConquestScreen.class),
Gauntlet(localizer.getMessageorUseDefault("lblGauntlet", "Gauntlet"), FSkinImage.MENU_GAUNTLET, NewGauntletScreen.class);
private final FMenuItem item;
private final Class<? extends FScreen> screenClass;
@@ -51,7 +51,7 @@ public class NewGameMenu extends FPopupMenu {
if (screen == null) { //don't initialize screen until it's opened the first time
try {
screen = screenClass.newInstance();
screen.setHeaderCaption(localizer.getMessage("lblNewGame") + " - " + item.getText());
screen.setHeaderCaption(localizer.getMessageorUseDefault("lblNewGame", "New Game") + " - " + item.getText());
}
catch (Exception e) {
e.printStackTrace();

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) {
@@ -474,6 +487,11 @@ public class MatchController extends AbstractGuiGame {
//view = null;
}
public void resetPlayerPanels() {
if (view != null)
view.forceRevalidate();
}
private static void actuateMatchPreferences() {
final ForgePreferences prefs = FModel.getPreferences();

View File

@@ -535,6 +535,13 @@ public class MatchScreen extends FScreen {
}
}
public void forceRevalidate() {
for (VPlayerPanel playerPanel : getPlayerPanels().values()) {
playerPanel.revalidate(true);
}
}
public void updateZones(final Iterable<PlayerZoneUpdate> zonesToUpdate) {
for (final PlayerZoneUpdate update : zonesToUpdate) {
final PlayerView owner = update.getPlayer();

View File

@@ -461,8 +461,10 @@ public class VAssignCombatDamage extends FDialog {
}
else {
lethalDamage = Math.max(0, source.getLethalDamage());
if (attackerHasDeathtouch) {
lethalDamage = Math.min(lethalDamage, 1);
if (source.getCurrentState().getType().isPlaneswalker()) {
lethalDamage = Integer.valueOf(source.getCurrentState().getLoyalty());
} else if (attackerHasDeathtouch) {
lethalDamage = Math.min(lethalDamage, 1);
}
}
return lethalDamage;

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

@@ -70,7 +70,7 @@ public class VAvatar extends FDisplayObject {
float h = getHeight();
g.drawImage(image, 0, 0, w, h);
if (Forge.altPlayerLayout && Forge.isLandscapeMode())
if (Forge.altPlayerLayout && !Forge.altZoneTabs && Forge.isLandscapeMode())
return;
//display XP in lower right corner of avatar

View File

@@ -15,6 +15,7 @@ public class VField extends FContainer {
private final FieldRow row1, row2;
private boolean flipped;
private float commandZoneWidth;
private float fieldModifier;
public VField(PlayerView player0) {
player = player0;
@@ -162,6 +163,10 @@ public class VField extends FContainer {
commandZoneWidth = commandZoneWidth0;
}
void setFieldModifier(float fieldModifierWidth) {
fieldModifier = fieldModifierWidth;
}
@Override
public void clear() {
row1.clear(); //clear rows instead of removing the rows
@@ -180,8 +185,8 @@ public class VField extends FContainer {
y1 = 0;
y2 = cardSize;
}
row1.setBounds(0, y1, width, cardSize);
row2.setBounds(0, y2, width - commandZoneWidth, cardSize);
row1.setBounds(0, y1, width-fieldModifier, cardSize);
row2.setBounds(0, y2, (width - commandZoneWidth)-fieldModifier, cardSize);
}
public class FieldRow extends VCardDisplayArea {

View File

@@ -30,6 +30,7 @@ import forge.util.Utils;
public class VPlayerPanel extends FContainer {
private static final FSkinFont LIFE_FONT = FSkinFont.get(18);
private static final FSkinFont LIFE_FONT_ALT = FSkinFont.get(22);
private static final FSkinFont INFO_FONT = FSkinFont.get(12);
private static final FSkinFont INFO2_FONT = FSkinFont.get(14);
private static final FSkinColor INFO_FORE_COLOR = FSkinColor.get(Colors.CLR_TEXT);
@@ -116,6 +117,18 @@ public class VPlayerPanel extends FContainer {
return null;
}
private boolean isAltZoneDisplay(InfoTab tab) {
if (tab.getIcon() == FSkinImage.HDHAND || tab.getIcon() == FSkinImage.HAND)
return true;
if (tab.getIcon() == FSkinImage.HDGRAVEYARD || tab.getIcon() == FSkinImage.GRAVEYARD)
return true;
if (tab.getIcon() == FSkinImage.HDLIBRARY || tab.getIcon() == FSkinImage.LIBRARY)
return true;
if (tab.getIcon() == FSkinImage.HDEXILE || tab.getIcon() == FSkinImage.EXILE)
return true;
return false;
}
public void setSelectedZone(ZoneType zoneType) {
setSelectedTab(zoneTabs.get(zoneType));
}
@@ -281,33 +294,51 @@ public class VPlayerPanel extends FContainer {
child.setTop(height - child.getBottom());
}
}
//this is used for landscape so set this to 0
field.setFieldModifier(0);
}
private void doLandscapeLayout(float width, float height) {
float x = 0;
float y = 0;
float yAlt = 0;
float avatarWidth = Forge.altZoneTabs ? avatar.getWidth() : 0;
avatar.setPosition(x, y);
y += avatar.getHeight();
lblLife.setBounds(x, Forge.altPlayerLayout ? 0 : y, avatar.getWidth(), Forge.altPlayerLayout ? INFO_FONT.getLineHeight() : LIFE_FONT.getLineHeight());
if (Forge.altPlayerLayout) {
lblLife.setBounds(x, (Forge.altPlayerLayout && !Forge.altZoneTabs) ? 0 : y, avatar.getWidth(), (Forge.altPlayerLayout && !Forge.altZoneTabs) ? INFO_FONT.getLineHeight() : Forge.altZoneTabs ? LIFE_FONT_ALT.getLineHeight() : LIFE_FONT.getLineHeight());
if (Forge.altPlayerLayout && !Forge.altZoneTabs) {
if (adjustHeight > 2)
y += INFO_FONT.getLineHeight()/2;
} else
y += lblLife.getHeight();
float infoTabWidth = avatar.getWidth();
float infoTabHeight = (height - y) / tabs.size();
int tabSize = !Forge.altZoneTabs ? tabs.size() : tabs.size() - 4;
float infoTabHeight = (height - y) / tabSize;
float infoTabHeightAlt = (height - yAlt) / 4;
for (InfoTab tab : tabs) {
tab.setBounds(x, y, infoTabWidth, infoTabHeight);
y += infoTabHeight;
if (!Forge.altZoneTabs) {
tab.setBounds(x, y, infoTabWidth, infoTabHeight);
y += infoTabHeight;
} else {
if (!isAltZoneDisplay(tab)) {
tab.setBounds(x, y, infoTabWidth, infoTabHeight);
y += infoTabHeight;
} else {
tab.setBounds(x+width-avatarWidth, yAlt, avatarWidth, infoTabHeightAlt);
yAlt += infoTabHeightAlt;
}
}
}
x = avatar.getRight();
phaseIndicator.resetFont();
phaseIndicator.setBounds(x, 0, avatar.getWidth() * 0.6f, height);
x += phaseIndicator.getWidth();
float fieldWidth = width - x;
float fieldWidth = width - x - avatarWidth;
float displayAreaWidth = height / FCardPanel.ASPECT_RATIO;
if (selectedTab != null) {
fieldWidth -= displayAreaWidth;
@@ -331,10 +362,15 @@ public class VPlayerPanel extends FContainer {
field.setBounds(x, 0, fieldWidth, height);
x = width - displayAreaWidth;
x = width - displayAreaWidth-avatarWidth;
for (InfoTab tab : tabs) {
tab.displayArea.setBounds(x, 0, displayAreaWidth, height);
}
if (!Forge.altZoneTabs)
field.setFieldModifier(0);
else
field.setFieldModifier(avatarWidth);
}
@Override
@@ -419,7 +455,7 @@ public class VPlayerPanel extends FContainer {
public void draw(Graphics g) {
adjustHeight = 1;
float divider = Gdx.app.getGraphics().getHeight() > 900 ? 1.2f : 2f;
if(Forge.altPlayerLayout && Forge.isLandscapeMode()) {
if(Forge.altPlayerLayout && !Forge.altZoneTabs && Forge.isLandscapeMode()) {
if (poisonCounters == 0 && energyCounters == 0 && experienceCounters == 0) {
g.fillRect(Color.DARK_GRAY, 0, 0, INFO2_FONT.getBounds(lifeStr).width+1, INFO2_FONT.getBounds(lifeStr).height+1);
g.drawText(lifeStr, INFO2_FONT, INFO_FORE_COLOR.getColor(), 0, 0, getWidth(), getHeight(), false, Align.left, false);
@@ -453,7 +489,7 @@ public class VPlayerPanel extends FContainer {
}
} else {
if (poisonCounters == 0 && energyCounters == 0) {
g.drawText(lifeStr, LIFE_FONT, INFO_FORE_COLOR, 0, 0, getWidth(), getHeight(), false, Align.center, true);
g.drawText(lifeStr, Forge.altZoneTabs ? LIFE_FONT_ALT : LIFE_FONT, INFO_FORE_COLOR, 0, 0, getWidth(), getHeight(), false, Align.center, true);
} else {
float halfHeight = getHeight() / 2;
float textStart = halfHeight + Utils.scale(1);

View File

@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import forge.gui.GuiBase;
import org.apache.commons.lang3.StringUtils;
import com.badlogic.gdx.utils.Align;
@@ -133,26 +134,28 @@ public class FilesPage extends TabPage<SettingsScreen> {
ForgeProfileProperties.setDecksDir(newDir);
}
};
lstItems.addItem(new StorageOption(localizer.getMessage("lblDataLocation"), ForgeProfileProperties.getUserDir()) {
@Override
protected void onDirectoryChanged(String newDir) {
ForgeProfileProperties.setUserDir(newDir);
if (!GuiBase.isUsingAppDirectory()) {
lstItems.addItem(new StorageOption(localizer.getMessage("lblDataLocation"), ForgeProfileProperties.getUserDir()) {
@Override
protected void onDirectoryChanged(String newDir) {
ForgeProfileProperties.setUserDir(newDir);
//ensure decks option is updated if needed
decksOption.updateDir(ForgeProfileProperties.getDecksDir());
}
}, 1);
lstItems.addItem(new StorageOption(localizer.getMessage("lblImageCacheLocation"), ForgeProfileProperties.getCacheDir()) {
@Override
protected void onDirectoryChanged(String newDir) {
ForgeProfileProperties.setCacheDir(newDir);
//ensure decks option is updated if needed
decksOption.updateDir(ForgeProfileProperties.getDecksDir());
}
}, 1);
lstItems.addItem(new StorageOption(localizer.getMessage("lblImageCacheLocation"), ForgeProfileProperties.getCacheDir()) {
@Override
protected void onDirectoryChanged(String newDir) {
ForgeProfileProperties.setCacheDir(newDir);
//ensure card pics option is updated if needed
cardPicsOption.updateDir(ForgeProfileProperties.getCardPicsDir());
}
}, 1);
lstItems.addItem(cardPicsOption, 1);
lstItems.addItem(decksOption, 1);
//ensure card pics option is updated if needed
cardPicsOption.updateDir(ForgeProfileProperties.getCardPicsDir());
}
}, 1);
lstItems.addItem(cardPicsOption, 1);
lstItems.addItem(decksOption, 1);
}
}
@Override

View File

@@ -27,6 +27,7 @@ import forge.screens.FScreen;
import forge.screens.TabPageScreen;
import forge.screens.TabPageScreen.TabPage;
import forge.screens.home.HomeScreen;
import forge.screens.match.MatchController;
import forge.sound.SoundSystem;
import forge.toolbox.FCheckBox;
import forge.toolbox.FGroupList;
@@ -248,8 +249,22 @@ public class SettingsPage extends TabPage<SettingsScreen> {
super.select();
//update
Forge.altPlayerLayout = FModel.getPreferences().getPrefBoolean(FPref.UI_ALT_PLAYERINFOLAYOUT);
if (MatchController.instance != null)
MatchController.instance.resetPlayerPanels();
}
},1);
lstSettings.addItem(new BooleanSetting(FPref.UI_ALT_PLAYERZONETABS,
localizer.getMessage("lblAltZoneTabs"),
localizer.getMessage("nlAltZoneTabs")){
@Override
public void select() {
super.select();
//update
Forge.altZoneTabs = FModel.getPreferences().getPrefBoolean(FPref.UI_ALT_PLAYERZONETABS);
if (MatchController.instance != null)
MatchController.instance.resetPlayerPanels();
}
},1);
//Random Deck Generation
lstSettings.addItem(new BooleanSetting(FPref.DECKGEN_NOSMALL,

View File

@@ -4,6 +4,7 @@ import static forge.card.CardRenderer.MANA_SYMBOL_SIZE;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import com.badlogic.gdx.utils.Align;
@@ -24,6 +25,7 @@ import forge.card.mana.ManaCostParser;
import forge.game.card.CardView;
import forge.game.card.IHasCardView;
import forge.game.player.PlayerView;
import forge.game.zone.ZoneType;
import forge.item.InventoryItem;
import forge.item.PaperCard;
import forge.itemmanager.AdvancedSearch.FilterOperator;
@@ -386,6 +388,8 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
}
//simple check for cardview needed on some special renderer for cards
private boolean showAlternate(CardView cardView, String value){
if(cardView == null)
return false;
boolean showAlt = false;
if(cardView.hasAlternateState()){
if(cardView.hasBackSide())
@@ -515,7 +519,8 @@ public class FChoiceList<T> extends FList<T> implements ActivateHandler {
@Override
public void drawValue(Graphics g, T value, FSkinFont font, FSkinColor foreColor, boolean pressed, float x, float y, float w, float h) {
CardView cv = ((IHasCardView)value).getCardView();
//should fix NPE ie Thief of Sanity, Gonti... etc
CardView cv = ((IHasCardView)value).getCardView().isFaceDown() && ((IHasCardView)value).getCardView().isInZone(EnumSet.of(ZoneType.Exile)) ? ((IHasCardView)value).getCardView().getBackup() : ((IHasCardView)value).getCardView();
boolean showAlternate = showAlternate(cv, value.toString());
CardRenderer.drawCardWithOverlays(g, cv, x, y, VStack.CARD_WIDTH, VStack.CARD_HEIGHT, CardStackPosition.Top, false, showAlternate, true);

View File

@@ -101,6 +101,13 @@ public abstract class FContainer extends FDisplayObject {
}
public void revalidate() {
revalidate(false);
}
public void revalidate(boolean forced) {
if (forced) {
doLayout(getWidth(), getHeight());
return;
}
float w = getWidth();
float h = getHeight();
if (w == 0 || h == 0) { return; } //don't revalidate if size not set yet