Merge remote-tracking branch 'origin/master' into code-cleanup

# Conflicts:
#	forge-ai/src/main/java/forge/ai/AiAttackController.java
#	forge-ai/src/main/java/forge/ai/AiCardMemory.java
#	forge-ai/src/main/java/forge/ai/AiController.java
#	forge-core/src/main/java/forge/card/ICardDatabase.java
#	forge-core/src/main/java/forge/item/generation/BoosterGenerator.java
#	forge-core/src/main/java/forge/util/FileSection.java
#	forge-core/src/main/java/forge/util/collect/FCollection.java
#	forge-game/src/main/java/forge/game/card/CardProperty.java
#	forge-game/src/main/java/forge/game/combat/Combat.java
#	forge-game/src/main/java/forge/game/spellability/SpellAbility.java
#	forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java
#	forge-gui-desktop/src/main/java/forge/screens/deckeditor/controllers/ACEditorBase.java
#	forge-gui-mobile/src/forge/itemmanager/ItemManager.java
#	forge-gui/src/main/java/forge/deck/DeckgenUtil.java
#	forge-gui/src/main/java/forge/gamemodes/limited/CardThemedDeckBuilder.java
This commit is contained in:
Jetz
2024-12-04 08:15:30 -05:00
632 changed files with 9973 additions and 6858 deletions

View File

@@ -159,6 +159,9 @@ public class Forge implements ApplicationListener {
public void create() {
//install our error handler
ExceptionHandler.registerErrorHandling();
// closeSplashScreen() is called early on non-Windows OS so it will not crash, LWJGL3 bug on AWT Splash.
if (OperatingSystem.isWindows())
getDeviceAdapter().closeSplashScreen();
GuiBase.setIsAndroid(Gdx.app.getType() == Application.ApplicationType.Android);
@@ -257,8 +260,6 @@ public class Forge implements ApplicationListener {
/* call preloadExtendedArt here, if we put it above we will *
* get error: No OpenGL context found in the current thread. */
preloadExtendedArt();
// should be after create method but try to close this at a later time.
getDeviceAdapter().closeSplashScreen();
});
};
//see if app or assets need updating

View File

@@ -5,6 +5,7 @@ import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
@@ -33,7 +34,7 @@ public class Graphics {
private static final int GL_BLEND = GL20.GL_BLEND;
private static final int GL_LINE_SMOOTH = 2848; //create constant here since not in GL20
private final SpriteBatch batch = new SpriteBatch();
private final Batch batch = new SpriteBatch();
private final ShapeRenderer shapeRenderer = new ShapeRenderer();
private final Deque<Matrix4> Dtransforms = new ArrayDeque<>();
private final Vector3 tmp = new Vector3();
@@ -114,7 +115,7 @@ public class Graphics {
if (dummyTexture != null) dummyTexture.dispose();
}
public SpriteBatch getBatch() {
public Batch getBatch() {
return batch;
}
@@ -317,24 +318,14 @@ public class Graphics {
}
public void drawLineArrow(float arrowThickness, FSkinColor skinColor, float x1, float y1, float x2, float y2) {
fillCircle(skinColor.getColor(), x2, y2, arrowThickness);
drawLineArrow(arrowThickness, skinColor.getColor(), x1, y1, x2, y2);
fillCircle(Color.WHITE, x2, y2, arrowThickness / 2);
drawLineArrow(arrowThickness / 3, Color.WHITE, x1, y1, x2, y2);
//drawLine(arrowThickness / 3, Color.WHITE, x1, y1, x2, y2);
}
public void drawLineArrow(float thickness, Color color, float x1, float y1, float x2, float y2) {
batch.end(); //must pause batch while rendering shapes
float ct = thickness / 2;
float lt = thickness / 3;
/*float angle = new Vector2(x1 - x2, y1 - y2).angleRad();
float arrowHeadRotation = (float) (Math.PI * 0.8f);
Vector2 arrowCorner3 = new Vector2(x2 + (thickness / 3) * (float) Math.cos(angle + arrowHeadRotation), y2 + (thickness / 3) * (float) Math.sin(angle + arrowHeadRotation));
Vector2 arrowCorner4 = new Vector2(x2 + (thickness / 3) * (float) Math.cos(angle - arrowHeadRotation), y2 + (thickness / 3) * (float) Math.sin(angle - arrowHeadRotation));*/
if (thickness > 1) {
Gdx.gl.glLineWidth(thickness);
}
if (alphaComposite < 1) {
color = FSkinColor.alphaColor(color, color.a * alphaComposite);
}
@@ -345,12 +336,29 @@ public class Graphics {
if (needSmoothing) {
Gdx.gl.glEnable(GL_LINE_SMOOTH);
}
startShape(ShapeType.Filled);
shapeRenderer.setColor(color);
shapeRenderer.circle(adjustX(x2), adjustY(y2, 0), thickness);
shapeRenderer.setColor(Color.WHITE);
shapeRenderer.circle(adjustX(x2), adjustY(y2, 0), ct);
endShape();
if (thickness > 1) {
Gdx.gl.glLineWidth(thickness);
}
startShape(ShapeType.Line);
shapeRenderer.setColor(color);
shapeRenderer.line(adjustX(x1), adjustY(y1, 0), adjustX(x2), adjustY(y2, 0));
endShape();
if (lt > 1) {
Gdx.gl.glLineWidth(lt);
}
startShape(ShapeType.Line);
shapeRenderer.setColor(Color.WHITE);
shapeRenderer.line(adjustX(x1), adjustY(y1, 0), adjustX(x2), adjustY(y2, 0));
endShape();
if (needSmoothing) {
Gdx.gl.glDisable(GL_LINE_SMOOTH);
}

View File

@@ -277,11 +277,8 @@ public class World implements Disposable, SaveFileContent {
for (int xclear = -size; xclear < size; xclear++)
for (int yclear = -size; yclear < size; yclear++) {
try {
terrainMap[x + xclear][height - 1 - (y + yclear)] = 0;
} catch (ArrayIndexOutOfBoundsException e) {
}
} catch (ArrayIndexOutOfBoundsException ignored) {}
}
}
@@ -344,6 +341,9 @@ public class World implements Disposable, SaveFileContent {
structure.initialize();
structureDataMap.put(data, structure);
return measureGenerationTime("wavefunctioncollapse " + data.sourcePath, threadStartTime);
}).exceptionally(ex -> {
ex.printStackTrace();
return 0L;
}));
}
}
@@ -648,7 +648,10 @@ public class World implements Disposable, SaveFileContent {
startY = startY + sy;
}
}
return 0l;
return 0L;
}).exceptionally(ex -> {
ex.printStackTrace();
return 0L;
}));
}
futuresArray = futures.toArray(new CompletableFuture<?>[0]);
@@ -689,7 +692,10 @@ public class World implements Disposable, SaveFileContent {
startY = startY + sy;
}
}
return 0l;
return 0L;
}).exceptionally(ex -> {
ex.printStackTrace();
return 0L;
}));
}
futuresArray = futures.toArray(new CompletableFuture<?>[0]);
@@ -931,7 +937,7 @@ public class World implements Disposable, SaveFileContent {
}
public int getChunkSize() {
return (Scene.getIntendedWidth() > Scene.getIntendedHeight() ? Scene.getIntendedWidth() : Scene.getIntendedHeight()) / data.tileSize;
return (Math.max(Scene.getIntendedWidth(), Scene.getIntendedHeight())) / data.tileSize;
}
public void dispose() {

View File

@@ -11,12 +11,9 @@ import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.*;
import com.badlogic.gdx.graphics.g2d.BitmapFont.BitmapFontData;
import com.badlogic.gdx.graphics.g2d.BitmapFont.Glyph;
import com.badlogic.gdx.graphics.g2d.PixmapPacker;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter;
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
@@ -323,7 +320,7 @@ public class FSkinFont {
return font.getLineHeight();
}
public void draw(SpriteBatch batch, String text, Color color, float x, float y, float w, boolean wrap, int horzAlignment) {
public void draw(Batch batch, String text, Color color, float x, float y, float w, boolean wrap, int horzAlignment) {
updateScale();
font.setColor(color);
font.draw(batch, text, x, y, w, horzAlignment, wrap);

View File

@@ -25,6 +25,8 @@ import java.util.Queue;
import java.util.Set;
import com.badlogic.gdx.graphics.Pixmap;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.EvictingQueue;
import com.google.common.collect.Queues;
import com.google.common.collect.Sets;
@@ -70,20 +72,7 @@ import forge.util.ImageUtil;
*/
public class ImageCache {
private static ImageCache imageCache;
private HashSet<String> _missingIconKeys;
private HashSet<String> missingIconKeys() {
HashSet<String> result = _missingIconKeys;
if (result == null) {
synchronized (this) {
result = _missingIconKeys;
if (result == null) {
result = new HashSet<>();
_missingIconKeys = result;
}
}
}
return _missingIconKeys;
}
private Supplier<HashSet<String>> missingIconKeys = Suppliers.memoize(HashSet::new);
private final List<String> borderlessCardlistKey = FileUtil.readFile(ForgeConstants.BORDERLESS_CARD_LIST_FILE);
public int counter = 0;
private int maxCardCapacity = 300; //default card capacity
@@ -133,20 +122,7 @@ public class ImageCache {
return Forge.getAssets().getDefaultImage();
}
private HashMap<String, ImageRecord> _imageRecord;
private HashMap<String, ImageRecord> imageRecord() {
HashMap<String, ImageRecord> result = _imageRecord;
if (result == null) {
synchronized (this) {
result = _imageRecord;
if (result == null) {
result = new HashMap<>(maxCardCapacity + (maxCardCapacity / 3));
_imageRecord = result;
}
}
}
return _imageRecord;
}
private Supplier<HashMap<String, ImageRecord>> imageRecord = Suppliers.memoize(() -> new HashMap<>(maxCardCapacity + (maxCardCapacity / 3)));
private boolean imageLoaded, delayLoadRequested;
public void allowSingleLoad() {
@@ -155,7 +131,7 @@ public class ImageCache {
}
public void clear() {
missingIconKeys().clear();
missingIconKeys.get().clear();
ImageKeys.clearMissingCards();
}
@@ -201,8 +177,8 @@ public class ImageCache {
public FImage getIcon(IHasIcon ihi) {
String imageKey = ihi.getIconImageKey();
final Texture icon;
if (missingIconKeys().contains(imageKey) || (icon = getImage(ihi.getIconImageKey(), false, true)) == null) {
missingIconKeys().add(imageKey);
if (missingIconKeys.get().contains(imageKey) || (icon = getImage(ihi.getIconImageKey(), false, true)) == null) {
missingIconKeys.get().add(imageKey);
return FSkinImage.UNKNOWN;
}
return new FTextureImage(icon);
@@ -317,8 +293,8 @@ public class ImageCache {
/*fix not loading image file since we intentionally not to update the cache in order for the
image fetcher to update automatically after the card image/s are downloaded*/
imageLoaded = false;
if (image != null && imageRecord().get(image.toString()) == null)
imageRecord().put(image.toString(), new ImageRecord(Color.valueOf("#171717").toString(), false, getRadius(image))); //black border
if (image != null && imageRecord.get().get(image.toString()) == null)
imageRecord.get().put(image.toString(), new ImageRecord(Color.valueOf("#171717").toString(), false, getRadius(image))); //black border
}
}
return image;
@@ -450,7 +426,7 @@ public class ImageCache {
if (t == null)
return Color.valueOf("#171717");
try {
return Color.valueOf(imageRecord().get(t.toString()).colorValue);
return Color.valueOf(imageRecord.get().get(t.toString()).colorValue);
} catch (Exception e) {
return Color.valueOf("#171717");
}
@@ -468,13 +444,13 @@ public class ImageCache {
return 0;
}
public void updateImageRecord(String textureString, String colorValue, Boolean isClosertoWhite, int radius) {
imageRecord().put(textureString, new ImageRecord(colorValue, isClosertoWhite, radius));
imageRecord.get().put(textureString, new ImageRecord(colorValue, isClosertoWhite, radius));
}
public int getRadius(Texture t) {
if (t == null)
return 20;
ImageRecord record = imageRecord().get(t.toString());
ImageRecord record = imageRecord.get().get(t.toString());
if (record == null)
return 20;
Integer i = record.cardRadius;
@@ -484,7 +460,7 @@ public class ImageCache {
}
public FImage getBorder(String textureString) {
ImageRecord record = imageRecord().get(textureString);
ImageRecord record = imageRecord.get().get(textureString);
if (record == null)
return FSkinImage.IMG_BORDER_BLACK;
Boolean border = record.isCloserToWhite;

View File

@@ -177,7 +177,7 @@ public class TextRenderer {
needClip = true;
}
}
addPiece(new SymbolPiece(symbol, inReminderTextCount > 0), lineNum, x, y - font.getAscent() + (lineHeight - pieceWidth) / 2, pieceWidth, pieceWidth);
addPiece(new SymbolPiece(symbol, inReminderTextCount > 0), lineNum, x, y - lineHeight / 4, pieceWidth, pieceWidth);
x += pieceWidth;
pieceWidth = 0;
text.setLength(0);

View File

@@ -377,9 +377,11 @@ public class CardZoom extends FOverlay {
float x = (w - cardWidth) / 2;
y = (h - cardHeight) / 2;
if (zoomMode) {
CardImageRenderer.drawZoom(g, currentCard, gameView, showBackSide? showBackSide : showAltState, x, y, cardWidth, cardHeight, getWidth(), getHeight(), true);
if (currentCard != null)
CardImageRenderer.drawZoom(g, currentCard, gameView, showBackSide? showBackSide : showAltState, x, y, cardWidth, cardHeight, getWidth(), getHeight(), true);
} else {
CardImageRenderer.drawDetails(g, currentCard, gameView, showBackSide ? showBackSide : showAltState, x, y, cardWidth, cardHeight);
if (currentCard != null)
CardImageRenderer.drawDetails(g, currentCard, gameView, showBackSide ? showBackSide : showAltState, x, y, cardWidth, cardHeight);
}
if (!showMerged) {

View File

@@ -9,6 +9,7 @@ import forge.Forge.KeyInputAdapter;
import forge.Graphics;
import forge.assets.*;
import forge.card.CardEdition;
import forge.card.MagicColor;
import forge.deck.io.DeckPreferences;
import forge.gamemodes.limited.BoosterDraft;
import forge.gamemodes.planarconquest.ConquestUtil;
@@ -1798,6 +1799,8 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
CardManagerPage cardSourceSection;
DeckSection destination = DeckSection.matchingSection(card);
final DeckSectionPage destinationPage = parentScreen.getPageForSection(destination);
// val for colorID setup
int val;
switch (deckSection) {
default:
case Main:
@@ -1840,6 +1843,19 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
}
}
addCommanderItems(menu, card);
if ((val = card.getRules().getSetColorID()) > 0) {
menu.addItem(new FMenuItem(Forge.getLocalizer().getMessage("lblColorIdentity"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, e -> {
//sort options so current option is on top and selected by default
Set<String> colorChoices = new HashSet<>(MagicColor.Constant.ONLY_COLORS);
GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseAColor", Lang.getNumeral(val)), val, val, colorChoices, new Callback<>() {
@Override
public void run(List<String> result) {
addCard(card.getColorIDVersion(new HashSet<>(result)));
removeCard(card);
}
});
}));
}
break;
case Sideboard:
cardSourceSection = parentScreen.isLimitedEditor() ? parentScreen.getMainDeckPage() : parentScreen.getCatalogPage();
@@ -1879,6 +1895,19 @@ public class FDeckEditor extends TabPageScreen<FDeckEditor> {
}
}
addCommanderItems(menu, card);
if ((val = card.getRules().getSetColorID()) > 0) {
menu.addItem(new FMenuItem(Forge.getLocalizer().getMessage("lblColorIdentity"), Forge.hdbuttons ? FSkinImage.HDPREFERENCE : FSkinImage.SETTINGS, e -> {
//sort options so current option is on top and selected by default
Set<String> colorChoices = new HashSet<>(MagicColor.Constant.ONLY_COLORS);
GuiChoose.getChoices(Forge.getLocalizer().getMessage("lblChooseAColor", Lang.getNumeral(val)), val, val, colorChoices, new Callback<>() {
@Override
public void run(List<String> result) {
addCard(card.getColorIDVersion(new HashSet<>(result)));
removeCard(card);
}
});
}));
}
break;
case Commander:
if (parentScreen.editorType != EditorType.PlanarConquest || isPartnerCommander(card)) {

View File

@@ -26,6 +26,8 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.Align;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import forge.Forge;
import forge.Graphics;
import forge.assets.FSkinColor;
@@ -64,22 +66,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
protected final ItemManagerModel<T> model;
private Predicate<? super T> filterPredicate = null;
private AdvancedSearchFilter<? extends T> advancedSearchFilter;
private List<ItemFilter<? extends T>> _filters;
private List<ItemFilter<? extends T>> filters() {
List<ItemFilter<? extends T>> result = _filters;
if (result == null) {
synchronized (this) {
result = _filters;
if (result == null) {
result = new ArrayList<>();
_filters = result;
}
}
}
return _filters;
}
private Supplier<List<ItemFilter<? extends T>>> filters = Suppliers.memoize(ArrayList::new);
private boolean hideFilters = false;
private boolean wantUnique = false;
private boolean showRanking = false;
@@ -92,22 +79,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
private ItemManagerConfig config;
private Function<Entry<? extends InventoryItem, Integer>, Object> fnNewGet;
private boolean viewUpdating, needSecondUpdate;
private List<ItemColumn> _sortCols;
private List<ItemColumn> sortCols() {
List<ItemColumn> result = _sortCols;
if (result == null) {
synchronized (this) {
result = _sortCols;
if (result == null) {
result = new ArrayList<>();
_sortCols = result;
}
}
}
return _sortCols;
}
private Supplier<List<ItemColumn>> sortCols = Suppliers.memoize(ArrayList::new);
private final TextSearchFilter<? extends T> searchFilter;
private CardFormatFilter cardFormatFilter;
@@ -146,16 +118,9 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
listView = new ItemListView<>(this, model);
imageView = createImageView(model);
if (Forge.isMobileAdventureMode) {
// reversed default
views.add(imageView);
views.add(listView);
currentView = imageView;
} else {
views.add(listView);
views.add(imageView);
currentView = listView;
}
views.add(listView);
views.add(imageView);
currentView = listView;
btnView.setIcon(currentView.getIcon());
//build display
@@ -248,7 +213,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
}
cols.sort(Comparator.comparingInt(arg0 -> arg0.getConfig().getIndex()));
sortCols().clear();
sortCols.get().clear();
if (cbxSortOptions != null) {
cbxSortOptions.setDropDownItemTap(null);
cbxSortOptions.removeAllItems();
@@ -258,14 +223,14 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
for (final ItemColumn col : cols) {
col.setIndex(modelIndex++);
if (col.isVisible()) {
sortCols().add(col);
sortCols.get().add(col);
}
}
final ItemColumn[] sortcols = new ItemColumn[sortCols().size()];
final ItemColumn[] sortcols = new ItemColumn[sortCols.get().size()];
// Assemble priority sort.
for (ItemColumn col : sortCols()) {
for (ItemColumn col : sortCols.get()) {
if (cbxSortOptions != null) {
cbxSortOptions.addItem(col);
}
@@ -382,7 +347,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
helper.offset(0, ItemFilter.PADDING);
// for Adventure Mode Store, Sideboard and Deck Event previews layout
if (ItemManagerConfig.ADVENTURE_STORE_POOL.equals(config) || ItemManagerConfig.ADVENTURE_SIDEBOARD.equals(config)) {
for (ItemFilter<? extends T> filter : filters()) {
for (ItemFilter<? extends T> filter : filters.get()) {
if (filter instanceof CardColorFilter) {
filter.getWidget().setVisible(true);
helper.include(filter.getWidget(), (viewButtonWidth + helper.getGapX()) * 7, fieldHeight);
@@ -405,7 +370,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
helper.fillLine(advancedSearchFilter.getWidget(), fieldHeight);
}
if (!hideFilters) {
for (ItemFilter<? extends T> filter : filters()) {
for (ItemFilter<? extends T> filter : filters.get()) {
helper.include(filter.getWidget(), filter.getPreferredWidth(helper.getRemainingLineWidth(), fieldHeight), fieldHeight);
}
if (allowSortChange()) {
@@ -695,7 +660,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
protected abstract AdvancedSearchFilter<? extends T> createAdvancedSearchFilter();
public void addFilter(final ItemFilter<? extends T> filter) {
filters().add(filter);
filters.get().add(filter);
add(filter.getWidget());
if (filter instanceof CardFormatFilter)
cardFormatFilter = (CardFormatFilter) filter;
@@ -729,10 +694,10 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
public void restoreDefaultFilters() {
lockFiltering = true;
for (ItemFilter<? extends T> filter : filters()) {
for (ItemFilter<? extends T> filter : filters.get()) {
remove(filter.getWidget());
}
filters().clear();
filters.get().clear();
addDefaultFilters();
lockFiltering = false;
revalidate();
@@ -741,7 +706,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
public void resetFilters() {
lockFiltering = true; //prevent updating filtering from this change until all filters reset
for (final ItemFilter<? extends T> filter : filters()) {
for (final ItemFilter<? extends T> filter : filters.get()) {
filter.reset();
}
searchFilter.reset();
@@ -759,7 +724,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
}
public void removeFilter(ItemFilter<? extends T> filter) {
filters().remove(filter);
filters.get().remove(filter);
remove(filter.getWidget());
revalidate();
applyFilters();
@@ -771,7 +736,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
}
List<Predicate<? super T>> predicates = new ArrayList<>();
for (ItemFilter<? extends T> filter : filters()) {
for (ItemFilter<? extends T> filter : filters.get()) {
if (!filter.isEmpty()) {
predicates.add(filter.buildPredicate(genericType));
}
@@ -822,7 +787,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
hideFilters = hideFilters0;
boolean visible = !hideFilters0;
for (ItemFilter<? extends T> filter : filters()) {
for (ItemFilter<? extends T> filter : filters.get()) {
filter.getWidget().setVisible(visible);
}
if (allowSortChange()) {
@@ -857,7 +822,7 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
ItemManager.this.add(advancedSearchFilter.getWidget());
}
lockFiltering = true;
for (final ItemFilter<? extends T> filter : filters()) {
for (final ItemFilter<? extends T> filter : filters.get()) {
filter.reset();
}
searchFilter.reset();
@@ -1132,9 +1097,9 @@ public abstract class ItemManager<T extends InventoryItem> extends FContainer im
if (cbxSortOptions != null) {
return cbxSortOptions.getWidth();
}
if (filters().isEmpty()) {
if (filters.get().isEmpty()) {
return 0f;
}
return filters().get(filters().size() - 1).getWidget().getWidth();
return filters.get().get(filters.get().size() - 1).getWidget().getWidth();
}
}

View File

@@ -5,6 +5,8 @@ import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Align;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import forge.Forge;
import forge.Forge.KeyInputAdapter;
import forge.Graphics;
@@ -36,6 +38,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static forge.assets.FSkin.getDefaultSkinFile;
@@ -44,6 +47,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
private static final float PADDING = Utils.scale(5);
private static final float PILE_SPACING_Y = 0.1f;
private static final FSkinFont LABEL_FONT = FSkinFont.get(12);
private TextRenderer textRenderer = new TextRenderer(true);
private static FSkinColor getGroupHeaderForeColor() {
if (Forge.isMobileAdventureMode)
@@ -63,20 +67,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
private static final int MIN_COLUMN_COUNT = Forge.isLandscapeMode() ? 2 : 1;
private static final int MAX_COLUMN_COUNT = 10;
private List<Integer> _selectedIndices;
private List<Integer> selectedIndices() {
List<Integer> result = _selectedIndices;
if (result == null) {
synchronized (this) {
result = _selectedIndices;
if (result == null) {
result = new ArrayList<>();
_selectedIndices = result;
}
}
}
return _selectedIndices;
}
private Supplier<List<Integer>> selectedIndices = Suppliers.memoize(ArrayList::new);
private int columnCount = 4;
private float scrollHeight = 0;
private ColumnDef pileBy = null;
@@ -84,34 +75,8 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
private ItemInfo focalItem;
private boolean updatingLayout;
private float totalZoomAmount;
private List<ItemInfo> _orderedItems;
private List<ItemInfo> orderedItems() {
List<ItemInfo> result = _orderedItems;
if (result == null) {
synchronized (this) {
result = _orderedItems;
if (result == null) {
result = new ArrayList<>();
_orderedItems = result;
}
}
}
return _orderedItems;
}
private List<Group> _groups;
private List<Group> groups() {
List<Group> result = _groups;
if (result == null) {
synchronized (this) {
result = _groups;
if (result == null) {
result = new ArrayList<>();
_groups = result;
}
}
}
return _groups;
}
private Supplier<List<ItemInfo>> orderedItems = Suppliers.memoize(ArrayList::new);
private Supplier<List<Group>> groups = Suppliers.memoize(ArrayList::new);
private class ExpandCollapseButton extends FLabel {
private boolean isAllCollapsed;
@@ -124,7 +89,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
}
boolean collapsed = !isAllCollapsed;
for (Group group : groups()) {
for (Group group : groups.get()) {
group.isCollapsed = collapsed;
}
@@ -136,7 +101,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
private void updateIsAllCollapsed() {
boolean isAllCollapsed0 = true;
for (Group group : groups()) {
for (Group group : groups.get()) {
if (!group.isCollapsed) {
isAllCollapsed0 = false;
break;
@@ -211,7 +176,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
getPnlOptions().add(cbPileByOptions);
Group group = new Group(""); //add default group
groups().add(group);
groups.get().add(group);
getScroller().add(group);
}
@@ -242,26 +207,26 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
cbGroupByOptions.setSelectedItem(groupBy);
}
groups().clear();
groups.get().clear();
if (groupBy == null) {
groups().add(new Group(""));
groups.get().add(new Group(""));
btnExpandCollapseAll.updateIsAllCollapsed();
} else {
for (String groupName : groupBy.getGroups()) {
groups().add(new Group(groupName));
groups.get().add(new Group(groupName));
}
//collapse all groups by default if all previous groups were collapsed
if (btnExpandCollapseAll.isAllCollapsed) {
for (Group group : groups()) {
for (Group group : groups.get()) {
group.isCollapsed = true;
}
}
}
getScroller().clear();
for (Group group : groups()) {
for (Group group : groups.get()) {
getScroller().add(group);
}
@@ -352,7 +317,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
//if not item hovered, use first fully visible item as focal point
final float visibleTop = getScrollValue();
for (Group group : groups()) {
for (Group group : groups.get()) {
if (group.getBottom() < visibleTop) {
continue;
}
@@ -367,10 +332,10 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
}
}
}
if (orderedItems().isEmpty()) {
if (orderedItems.get().isEmpty()) {
return null;
}
return orderedItems().get(0);
return orderedItems.get().get(0);
}
@Override
@@ -380,9 +345,9 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
@Override
protected void onRefresh() {
Group otherItems = groupBy == null ? groups().get(0) : null;
Group otherItems = groupBy == null ? groups.get().get(0) : null;
for (Group group : groups()) {
for (Group group : groups.get()) {
group.items.clear();
}
clearSelection();
@@ -395,19 +360,19 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
Group group;
if (groupIndex >= 0) {
if (groupIndex >= groups().size())
group = groups().get(groups().size() - 1);
if (groupIndex >= groups.get().size())
group = groups.get().get(groups.get().size() - 1);
else
group = groups().get(groupIndex);
group = groups.get().get(groupIndex);
} else {
if (otherItems == null) {
//reuse existing Other group if possible
if (groups().size() > groupBy.getGroups().length) {
otherItems = groups().get(groups().size() - 1);
if (groups.get().size() > groupBy.getGroups().length) {
otherItems = groups.get().get(groups.get().size() - 1);
} else {
otherItems = new Group(Forge.getLocalizer().getMessage("lblOther"));
otherItems.isCollapsed = btnExpandCollapseAll.isAllCollapsed;
groups().add(otherItems);
groups.get().add(otherItems);
}
}
group = otherItems;
@@ -419,10 +384,10 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
}
}
if (otherItems == null && groups().size() > groupBy.getGroups().length) {
int index = groups().size() - 1;
if (index < groups().size() && index >= 0)
groups().remove(index); //remove Other group if empty
if (otherItems == null && groups.get().size() > groupBy.getGroups().length) {
int index = groups.get().size() - 1;
if (index < groups.get().size() && index >= 0)
groups.get().remove(index); //remove Other group if empty
btnExpandCollapseAll.updateIsAllCollapsed();
}
@@ -470,8 +435,8 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
float dx = itemWidth + gap;
float dy = pileBy == null ? itemHeight + gap : itemHeight * PILE_SPACING_Y;
for (int i = 0; i < groups().size(); i++) {
Group group = groups().get(i);
for (int i = 0; i < groups.get().size(); i++) {
Group group = groups.get().get(i);
if (forRefresh && pileBy != null) { //refresh piles if needed
//use TreeMap to build pile set so iterating below sorts on key
@@ -564,8 +529,8 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
if (forRefresh) { //refresh ordered items if needed
int index = 0;
orderedItems().clear();
for (Group group : groups()) {
orderedItems.get().clear();
for (Group group : groups.get()) {
if (group.isCollapsed || group.items.isEmpty()) {
continue;
}
@@ -573,7 +538,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
for (Pile pile : group.piles) {
for (ItemInfo itemInfo : pile.items) {
itemInfo.index = index++;
orderedItems().add(itemInfo);
orderedItems.get().add(itemInfo);
}
}
}
@@ -623,11 +588,11 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
private ItemInfo getItemAtPoint(float x, float y) {
//check selected items first since they appear on top
for (int i = selectedIndices().size() - 1; i >= 0; i--) {
int currentIndex = selectedIndices().get(i);
if (currentIndex < 0 || orderedItems().size() <= currentIndex)
for (int i = selectedIndices.get().size() - 1; i >= 0; i--) {
int currentIndex = selectedIndices.get().get(i);
if (currentIndex < 0 || orderedItems.get().size() <= currentIndex)
continue;
ItemInfo item = orderedItems().get(currentIndex);
ItemInfo item = orderedItems.get().get(currentIndex);
float relX = x + item.group.getScrollLeft() - item.group.getLeft();
float relY = y + getScrollValue();
if (item.contains(relX, relY)) {
@@ -635,8 +600,8 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
}
}
for (int i = groups().size() - 1; i >= 0; i--) {
Group group = groups().get(i);
for (int i = groups.get().size() - 1; i >= 0; i--) {
Group group = groups.get().get(i);
if (!group.isCollapsed) {
for (int j = group.piles.size() - 1; j >= 0; j--) {
float relX = x + group.getScrollLeft() - group.getLeft();
@@ -659,14 +624,14 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
@Override
public T getItemAtIndex(int index) {
if (index >= 0 && index < getCount()) {
return orderedItems().get(index).item;
return orderedItems.get().get(index).item;
}
return null;
}
@Override
public int getIndexOfItem(T item) {
for (Group group : groups()) {
for (Group group : groups.get()) {
for (ItemInfo itemInfo : group.items) {
if (itemInfo.item == item) {
//if group containing item is collapsed, expand it so the item can be selected and has a valid index
@@ -685,22 +650,22 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
@Override
public int getSelectedIndex() {
return selectedIndices().isEmpty() ? -1 : selectedIndices().get(0);
return selectedIndices.get().isEmpty() ? -1 : selectedIndices.get().get(0);
}
@Override
public Iterable<Integer> getSelectedIndices() {
return selectedIndices();
return selectedIndices.get();
}
@Override
public int getCount() {
return orderedItems().size();
return orderedItems.get().size();
}
@Override
public int getSelectionCount() {
return selectedIndices().size();
return selectedIndices.get().size();
}
@Override
@@ -728,7 +693,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
@Override
public void selectAll() {
clearSelection();
IntStream.range(0, getCount()).forEach(selectedIndices()::add);
IntStream.range(0, getCount()).forEach(selectedIndices.get()::add);
updateSelection();
onSelectionChange();
}
@@ -736,7 +701,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
@Override
protected void onSetSelectedIndex(int index) {
clearSelection();
selectedIndices().add(index);
selectedIndices.get().add(index);
updateSelection();
}
@@ -744,24 +709,24 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
protected void onSetSelectedIndices(Iterable<Integer> indices) {
clearSelection();
for (Integer index : indices) {
selectedIndices().add(index);
selectedIndices.get().add(index);
}
updateSelection();
}
private void clearSelection() {
int count = getCount();
for (Integer i : selectedIndices()) {
for (Integer i : selectedIndices.get()) {
if (i < count) {
orderedItems().get(i).selected = false;
orderedItems.get().get(i).selected = false;
}
}
selectedIndices().clear();
selectedIndices.get().clear();
}
private void updateSelection() {
for (Integer i : selectedIndices()) {
orderedItems().get(i).selected = true;
for (Integer i : selectedIndices.get()) {
orderedItems.get().get(i).selected = true;
}
}
@@ -777,9 +742,9 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
}
if (item.selected) { //unselect item if already selected
if (selectedIndices().size() > minSelections) {
if (selectedIndices.get().size() > minSelections) {
item.selected = false;
selectedIndices().remove((Object) item.index);
selectedIndices.get().remove((Object) item.index);
onSelectionChange();
item.group.scrollIntoView(item);
}
@@ -788,8 +753,8 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
if (maxSelections <= 1 || (!KeyInputAdapter.isCtrlKeyDown() && !KeyInputAdapter.isShiftKeyDown())) {
clearSelection();
}
if (selectedIndices().size() < maxSelections) {
selectedIndices().add(0, item.index);
if (selectedIndices.get().size() < maxSelections) {
selectedIndices.get().add(0, item.index);
item.selected = true;
onSelectionChange();
item.group.scrollIntoView(item);
@@ -800,29 +765,29 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
@Override
public void scrollSelectionIntoView() {
if (selectedIndices().isEmpty()) {
if (selectedIndices.get().isEmpty()) {
return;
}
int index = selectedIndices().get(0);
if (index < 0 || orderedItems().size() <= index) {
int index = selectedIndices.get().get(0);
if (index < 0 || orderedItems.get().size() <= index) {
return;
}
ItemInfo itemInfo = orderedItems().get(index);
ItemInfo itemInfo = orderedItems.get().get(index);
getScroller().scrollIntoView(itemInfo);
}
@Override
public Rectangle getSelectionBounds() {
if (selectedIndices().isEmpty()) {
if (selectedIndices.get().isEmpty()) {
return new Rectangle();
}
int index = selectedIndices().get(0);
if (index < 0 || orderedItems().size() <= index) {
int index = selectedIndices.get().get(0);
if (index < 0 || orderedItems.get().size() <= index) {
return new Rectangle();
}
ItemInfo itemInfo = orderedItems().get(index);
ItemInfo itemInfo = orderedItems.get().get(index);
Vector2 relPos = itemInfo.group.getChildRelativePosition(itemInfo);
return new Rectangle(itemInfo.group.screenPos.x + relPos.x - SEL_BORDER_SIZE + itemInfo.group.getLeft(),
itemInfo.group.screenPos.y + relPos.y - SEL_BORDER_SIZE,
@@ -831,21 +796,21 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
@Override
public void zoomSelected() {
if (selectedIndices().isEmpty()) {
if (selectedIndices.get().isEmpty()) {
return;
}
int index = selectedIndices().get(0);
if (index < 0 || orderedItems().size() <= index) {
int index = selectedIndices.get().get(0);
if (index < 0 || orderedItems.get().size() <= index) {
return;
}
ItemInfo itemInfo = orderedItems().get(index);
ItemInfo itemInfo = orderedItems.get().get(index);
if (itemInfo != null) {
if (itemInfo.getKey() instanceof CardThemedDeckGenerator || itemInfo.getKey() instanceof CommanderDeckGenerator
|| itemInfo.getKey() instanceof ArchetypeDeckGenerator || itemInfo.getKey() instanceof DeckProxy) {
FDeckViewer.show(((DeckProxy) itemInfo.getKey()).getDeck());
}
CardZoom.show(orderedItems(), orderedItems().indexOf(itemInfo), itemManager);
CardZoom.show(orderedItems.get(), orderedItems.get().indexOf(itemInfo), itemManager);
}
}
@@ -975,7 +940,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
FDeckViewer.show(((DeckProxy) item.getKey()).getDeck());
return true;
}
CardZoom.show(orderedItems(), orderedItems().indexOf(item), itemManager);
CardZoom.show(orderedItems.get(), orderedItems.get().indexOf(item), itemManager);
return true;
}
return false;
@@ -1029,6 +994,7 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
private boolean selected, deckSelectMode, showRanking;
private final float IMAGE_SIZE = CardRenderer.MANA_SYMBOL_SIZE;
private DeckProxy deckProxy = null;
private String colorID = null;
private FImageComplex deckCover = null;
private Texture dpImg = null;
//private TextureRegion tr;
@@ -1055,6 +1021,9 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
draftRankImage = FSkinImage.DRAFTRANK_C;
}
}
if (((PaperCard) item).getColorID() != null) {
colorID = ((PaperCard) item).getColorID().stream().map(MagicColor::toSymbol).collect(Collectors.joining());
}
}
}
@@ -1136,6 +1105,10 @@ public class ImageView<T extends InventoryItem> extends ItemView<T> {
}
}
}
// spire colors
if (colorID != null && !colorID.isEmpty()) {
textRenderer.drawText(g, colorID, FSkinFont.forHeight(w / 5), Color.WHITE, x, y + h / 4, w, h, y, h, false, Align.center, true);
}
} else if (item instanceof ConquestCommander) {
CardRenderer.drawCard(g, ((ConquestCommander) item).getCard(), x, y, w, h, pos);
} else if (deckSelectMode) {

View File

@@ -32,7 +32,6 @@ import forge.assets.FSkinColor.Colors;
import forge.assets.FSkinTexture;
import forge.game.GameView;
import forge.game.card.CardView;
import forge.game.combat.CombatView;
import forge.game.phase.PhaseType;
import forge.game.player.PlayerView;
import forge.game.zone.ZoneType;
@@ -91,6 +90,11 @@ public class MatchScreen extends FScreen {
private static List<FDisplayObject> potentialListener;
private int selectedPlayer;
private final Map<Integer, Vector2> endpoints;
private final Set<CardView> cardsonBattlefield;
private final Set<PlayerView> playerViewSet;
public MatchScreen(List<VPlayerPanel> playerPanels0) {
super(new FMenuBar());
@@ -164,6 +168,9 @@ public class MatchScreen extends FScreen {
log.setMenuTab(new HiddenMenuTab(log));
devMenu.setMenuTab(new HiddenMenuTab(devMenu));
}
endpoints = new HashMap<>();
cardsonBattlefield = new HashSet<>();
playerViewSet = new HashSet<>();
}
private boolean is4Player() {
@@ -392,8 +399,7 @@ public class MatchScreen extends FScreen {
if (Forge.isLandscapeMode() && (!GuiBase.isAndroid() || Forge.hasGamepad()) && !CardZoom.isOpen() && potentialListener != null) {
for (FDisplayObject object : potentialListener) {
if (object != null) {
if (object instanceof FCardPanel) {
FCardPanel cardPanel = (FCardPanel) object;
if (object instanceof FCardPanel cardPanel) {
try {
if (cardPanel.isHovered()) {
VPlayerPanel vPlayerPanel = getPlayerPanel(cardPanel.getCard().getController());
@@ -431,9 +437,9 @@ public class MatchScreen extends FScreen {
} catch (Exception e) {
e.printStackTrace();
}
} else if (object instanceof VStack.StackInstanceDisplay) {
} else if (object instanceof VStack.StackInstanceDisplay vstackDisplay) {
try {
CardView cardView = ((VStack.StackInstanceDisplay) object).stackInstance.getSourceCard();
CardView cardView = vstackDisplay.stackInstance.getSourceCard();
if (object.isHovered() && cardView != null && getStack().isVisible()) {
float cardW = getHeight() * 0.45f;
float cardH = FCardPanel.ASPECT_RATIO * cardW;
@@ -459,30 +465,40 @@ public class MatchScreen extends FScreen {
}
void drawArcs(Graphics g) {
//get all card targeting arrow origins on the battlefield
final Map<Integer, Vector2> endpoints = new HashMap<>();
final Set<CardView> cardsonBattlefield = new HashSet<>();
final Set<PlayerView> playerViewSet = new HashSet<>();
final GameView game = MatchController.instance.getGameView();
if (game == null)
return;
//get all card targeting arrow origins on the battlefield
endpoints.clear();
cardsonBattlefield.clear();
playerViewSet.clear();
try {
for (PlayerView p : game.getPlayers()) {
if (p != null && playerPanelsList.contains(getPlayerPanel(p))) {
if (p == null)
continue;
VPlayerPanel playerPanel = getPlayerPanel(p);
if (playerPanel != null && playerPanelsList.contains(playerPanel)) {
playerViewSet.add(p);
if (p.getBattlefield() != null) {
for (CardView c : p.getBattlefield()) {
endpoints.put(c.getId(), CardAreaPanel.get(c).getTargetingArrowOrigin());
CardAreaPanel panel = CardAreaPanel.get(c);
Vector2 origin = panel.getTargetingArrowOrigin();
//outside left bounds
if (origin.x < playerPanel.getField().getLeft())
continue;
//outside right bounds
if (origin.x > playerPanel.getField().getRight())
continue;
endpoints.put(c.getId(), origin);
cardsonBattlefield.add(c);
}
}
}
}
//draw arrows for combat
final CombatView combat = game.getCombat();
for (CardView c : cardsonBattlefield) {
TargetingOverlay.assembleArrows(g, c, endpoints, combat, playerViewSet);
}
} catch (Exception e) {
}
if (endpoints.isEmpty())
return;
TargetingOverlay.assembleArrows(g, cardsonBattlefield, endpoints, game.getCombat(), playerViewSet);
} catch (Exception ignored) {}
}
@Override
@@ -511,7 +527,7 @@ public class MatchScreen extends FScreen {
}
}
revalidate(true);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
break;
@@ -525,7 +541,7 @@ public class MatchScreen extends FScreen {
selectedPlayerPanel().getSelectedRow().setNextSelected(1);
}
revalidate(true);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
break;
@@ -549,7 +565,7 @@ public class MatchScreen extends FScreen {
}
}
revalidate(true);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
break;
@@ -563,7 +579,7 @@ public class MatchScreen extends FScreen {
selectedPlayerPanel().getSelectedRow().setPreviousSelected(1);
}
revalidate(true);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
break;
@@ -576,7 +592,7 @@ public class MatchScreen extends FScreen {
} else {
selectedPlayerPanel().getSelectedRow().showZoom();
}
} catch (Exception e) {
} catch (Exception ignored) {
}
}
break;
@@ -591,7 +607,7 @@ public class MatchScreen extends FScreen {
//nullPotentialListener();
selectedPlayerPanel().getSelectedRow().tapChild();
}
} catch (Exception e) {
} catch (Exception ignored) {
}
}
break;
@@ -615,7 +631,7 @@ public class MatchScreen extends FScreen {
return getActivePrompt().getBtnCancel().trigger(); //trigger Cancel if can't trigger OK
case Keys.ESCAPE:
if (!FModel.getPreferences().getPrefBoolean(FPref.UI_ALLOW_ESC_TO_END_TURN) && !Forge.hasGamepad()) {//bypass check
if (getActivePrompt().getBtnCancel().getText().equals(Forge.getLocalizer().getInstance().getMessage("lblEndTurn"))) {
if (getActivePrompt().getBtnCancel().getText().equals(Forge.getLocalizer().getMessage("lblEndTurn"))) {
return false;
}
}
@@ -796,12 +812,11 @@ public class MatchScreen extends FScreen {
}
public void updateSingleCard(final CardView card) {
final CardAreaPanel pnl = CardAreaPanel.get(card);
if (pnl == null) {
if (card == null)
return;
}
final CardAreaPanel pnl = CardAreaPanel.get(card);
final ZoneType zone = card.getZone();
if (zone != null && zone == ZoneType.Battlefield) {
if (zone == ZoneType.Battlefield) {
pnl.updateCard(card);
} else { //ensure card not on battlefield is reset such that it no longer thinks it's on the battlefield
pnl.setTapped(false);
@@ -818,28 +833,18 @@ public class MatchScreen extends FScreen {
FSkinTexture getBG() {
if (Forge.isMobileAdventureMode) {
switch (GameScene.instance().getAdventurePlayerLocation(false, true)) {
case "green":
return FSkinTexture.ADV_BG_FOREST;
case "black":
return FSkinTexture.ADV_BG_SWAMP;
case "red":
return FSkinTexture.ADV_BG_MOUNTAIN;
case "blue":
return FSkinTexture.ADV_BG_ISLAND;
case "white":
return FSkinTexture.ADV_BG_PLAINS;
case "waste":
return FSkinTexture.ADV_BG_WASTE;
case "cave":
return FSkinTexture.ADV_BG_CAVE;
case "dungeon":
return FSkinTexture.ADV_BG_DUNGEON;
case "castle":
return FSkinTexture.ADV_BG_CASTLE;
default:
return FSkinTexture.ADV_BG_COMMON;
}
return switch (GameScene.instance().getAdventurePlayerLocation(false, true)) {
case "green" -> FSkinTexture.ADV_BG_FOREST;
case "black" -> FSkinTexture.ADV_BG_SWAMP;
case "red" -> FSkinTexture.ADV_BG_MOUNTAIN;
case "blue" -> FSkinTexture.ADV_BG_ISLAND;
case "white" -> FSkinTexture.ADV_BG_PLAINS;
case "waste" -> FSkinTexture.ADV_BG_WASTE;
case "cave" -> FSkinTexture.ADV_BG_CAVE;
case "dungeon" -> FSkinTexture.ADV_BG_DUNGEON;
case "castle" -> FSkinTexture.ADV_BG_CASTLE;
default -> FSkinTexture.ADV_BG_COMMON;
};
}
return FSkinTexture.BG_MATCH;
}
@@ -952,7 +957,7 @@ public class MatchScreen extends FScreen {
if (!hasActivePlane())
return;
}
boolean isGameFast = MatchController.instance.isGameFast();
//boolean isGameFast = MatchController.instance.isGameFast(); //this used to control animation speed
float midField = topPlayerPanel.getBottom();
float promptHeight = !Forge.isLandscapeMode() || bottomPlayerPrompt == null ? 0f : bottomPlayerPrompt.getHeight() / 1.3f;
float x = topPlayerPanel.getField().getLeft();

View File

@@ -68,50 +68,54 @@ public class TargetingOverlay {
private TargetingOverlay() {
}
public static void assembleArrows(final Graphics g, final CardView c, final Map<Integer, Vector2> endpoints, final CombatView combat, final Set<PlayerView> playerViewSet) {
final CardView attachedTo = c.getAttachedTo();
final Iterable<CardView> attachedCards = c.getAttachedCards();
final CardView paired = c.getPairedWith();
if (null != attachedTo) {
if (attachedTo.getController() != null && !attachedTo.getController().equals(c.getController())) {
public static void assembleArrows(final Graphics g, final Set<CardView> cardsonBattlefield, final Map<Integer, Vector2> endpoints, final CombatView combat, final Set<PlayerView> playerViewSet) {
if (cardsonBattlefield.isEmpty())
return;
for (CardView c : cardsonBattlefield) {
final CardView attachedTo = c.getAttachedTo();
final Iterable<CardView> attachedCards = c.getAttachedCards();
final CardView paired = c.getPairedWith();
if (null != attachedTo) {
if (attachedTo.getController() != null && !attachedTo.getController().equals(c.getController())) {
drawArrow(g, endpoints.get(attachedTo.getId()), endpoints.get(c.getId()), ArcConnection.Friends);
}
}
if (null != attachedTo && c == attachedTo.getAttachedTo()) {
drawArrow(g, endpoints.get(attachedTo.getId()), endpoints.get(c.getId()), ArcConnection.Friends);
}
}
if (null != attachedTo && c == attachedTo.getAttachedTo()) {
drawArrow(g, endpoints.get(attachedTo.getId()), endpoints.get(c.getId()), ArcConnection.Friends);
}
if (null != attachedCards) {
for (final CardView enc : attachedCards) {
if (enc.getController() != null && !enc.getController().equals(c.getController())) {
drawArrow(g, endpoints.get(c.getId()), endpoints.get(enc.getId()), ArcConnection.Friends);
if (null != attachedCards) {
for (final CardView enc : attachedCards) {
if (enc.getController() != null && !enc.getController().equals(c.getController())) {
drawArrow(g, endpoints.get(c.getId()), endpoints.get(enc.getId()), ArcConnection.Friends);
}
}
}
}
if (null != paired) {
drawArrow(g, endpoints.get(paired.getId()), endpoints.get(c.getId()), ArcConnection.Friends);
}
if (null != combat) {
final GameEntityView defender = combat.getDefender(c);
// if c is attacking a planeswalker or battle
if (defender instanceof CardView) {
drawArrow(g, endpoints.get(defender.getId()), endpoints.get(c.getId()), ArcConnection.FoesAttacking);
if (null != paired) {
drawArrow(g, endpoints.get(paired.getId()), endpoints.get(c.getId()), ArcConnection.Friends);
}
// if c is a planeswalker that's being attacked
for (final CardView pwAttacker : combat.getAttackersOf(c)) {
drawArrow(g, endpoints.get(c.getId()), endpoints.get(pwAttacker.getId()), ArcConnection.FoesAttacking);
}
for (final CardView attackingCard : combat.getAttackers()) {
final Iterable<CardView> cards = combat.getPlannedBlockers(attackingCard);
if (cards == null) continue;
for (final CardView blockingCard : cards) {
if (!attackingCard.equals(c) && !blockingCard.equals(c)) { continue; }
drawArrow(g, endpoints.get(attackingCard.getId()), endpoints.get(blockingCard.getId()), ArcConnection.FoesBlocking);
if (null != combat) {
final GameEntityView defender = combat.getDefender(c);
// if c is attacking a planeswalker or battle
if (defender instanceof CardView) {
drawArrow(g, endpoints.get(defender.getId()), endpoints.get(c.getId()), ArcConnection.FoesAttacking);
}
if (playerViewSet != null) {
for (final PlayerView p : playerViewSet) {
if (combat.getAttackersOf(p).contains(attackingCard)) {
final Vector2 vPlayer = MatchController.getView().getPlayerPanel(p).getAvatar().getTargetingArrowOrigin();
drawArrow(g, endpoints.get(attackingCard.getId()), vPlayer, TargetingOverlay.ArcConnection.FoesAttacking);
// if c is a planeswalker that's being attacked
for (final CardView pwAttacker : combat.getAttackersOf(c)) {
drawArrow(g, endpoints.get(c.getId()), endpoints.get(pwAttacker.getId()), ArcConnection.FoesAttacking);
}
for (final CardView attackingCard : combat.getAttackers()) {
final Iterable<CardView> cards = combat.getPlannedBlockers(attackingCard);
if (cards == null) continue;
for (final CardView blockingCard : cards) {
if (!attackingCard.equals(c) && !blockingCard.equals(c)) { continue; }
drawArrow(g, endpoints.get(attackingCard.getId()), endpoints.get(blockingCard.getId()), ArcConnection.FoesBlocking);
}
if (playerViewSet != null) {
for (final PlayerView p : playerViewSet) {
if (combat.getAttackersOf(p).contains(attackingCard)) {
final Vector2 vPlayer = MatchScreen.getPlayerPanel(p).getAvatar().getTargetingArrowOrigin();
drawArrow(g, endpoints.get(attackingCard.getId()), vPlayer, TargetingOverlay.ArcConnection.FoesAttacking);
}
}
}
}

View File

@@ -70,7 +70,6 @@ public class VAssignGenericAmount extends FDialog {
/** 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
@@ -168,7 +167,7 @@ public class VAssignGenericAmount extends FDialog {
obj = add(new EffectSourcePanel((CardView)entity));
} else if (entity instanceof PlayerView) {
PlayerView player = (PlayerView)entity;
obj = add(new MiscTargetPanel(player.getName(), MatchController.getPlayerAvatar(player)));
obj = add(new MiscTargetPanel(player.getName(), MatchController.getPlayerAvatar(player), null));
} else if (entity instanceof Byte) {
FSkinImageInterface manaSymbol;
byte color = (Byte) entity;
@@ -185,9 +184,9 @@ public class VAssignGenericAmount extends FDialog {
} else { // Should never come here, but add this to avoid compile error
manaSymbol = Forge.getAssets().images().get(FSkinProp.IMG_MANA_COLORLESS);
}
obj = add(new MiscTargetPanel("", manaSymbol));
obj = add(new MiscTargetPanel("", manaSymbol, entity));
} else {
obj = add(new MiscTargetPanel(entity.toString(), FSkinImage.UNKNOWN));
obj = add(new MiscTargetPanel(entity.toString(), FSkinImage.UNKNOWN, null));
}
label = add(new FLabel.Builder().text("0").font(FSkinFont.get(18)).align(Align.center).build());
btnSubtract = add(new FLabel.ButtonBuilder().icon(FSkinImage.MINUS).command(e -> assignAmountTo(entity, false)).build());
@@ -232,19 +231,21 @@ public class VAssignGenericAmount extends FDialog {
}
}
private static class MiscTargetPanel extends FDisplayObject {
private static final FSkinFont FONT = FSkinFont.get(18);
private static FSkinColor getForeColor() {
private class MiscTargetPanel extends FDisplayObject {
private final FSkinFont FONT = FSkinFont.get(18);
private FSkinColor getForeColor() {
if (Forge.isMobileAdventureMode)
return FSkinColor.get(Colors.ADV_CLR_TEXT);
return FSkinColor.get(Colors.CLR_TEXT);
}
private final String name;
private final FImage image;
private final Object entity;
private MiscTargetPanel(String name0, FImage image0) {
private MiscTargetPanel(String name0, FImage image0, Object entity0) {
name = name0;
image = image0;
entity = entity0;
}
@Override
@@ -254,6 +255,24 @@ public class VAssignGenericAmount extends FDialog {
g.drawImage(image, 0, 0, w, w);
g.drawText(name, FONT, getForeColor(), 0, w, w, h - w, false, Align.center, true);
}
@Override
public boolean tap(float x, float y, int count) {
if (count > 1 && entity != null) {
AssignTarget at = targetsMap.get(entity);
int assigned = at.amount;
int leftToAssign = Math.max(0, at.max - assigned);
int amountToAdd = Math.min(getRemainingAmount(), leftToAssign);
if (0 == amountToAdd || amountToAdd + assigned < 0) {
return false;
}
addAssignedAmount(at, amountToAdd);
updateLabels();
}
return super.tap(x, y, count);
}
}
private void assignAmountTo(Object source, boolean isAdding) {

View File

@@ -9,6 +9,8 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.math.Vector2;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import forge.Forge;
import forge.Graphics;
import forge.card.CardRenderer.CardStackPosition;
@@ -28,47 +30,21 @@ import io.sentry.Sentry;
public abstract class VCardDisplayArea extends VDisplayArea implements ActivateHandler {
private static final float CARD_STACK_OFFSET = 0.2f;
protected List<CardView> _orderedCards;
protected List<CardView> orderedCards() {
List<CardView> result = _orderedCards;
if (result == null) {
synchronized (this) {
result = _orderedCards;
if (result == null) {
result = new ArrayList<>();
_orderedCards = result;
}
}
}
return _orderedCards;
}
protected List<CardAreaPanel> _cardPanels;
protected List<CardAreaPanel> cardPanels() {
List<CardAreaPanel> result = _cardPanels;
if (result == null) {
synchronized (this) {
result = _cardPanels;
if (result == null) {
result = new ArrayList<>();
_cardPanels = result;
}
}
}
return _cardPanels;
}
protected Supplier<List<CardView>> orderedCards = Suppliers.memoize(ArrayList::new);
protected Supplier<List<CardAreaPanel>> cardPanels = Suppliers.memoize(ArrayList::new);
private boolean rotateCards180;
public Iterable<CardView> getOrderedCards() {
return orderedCards();
return orderedCards.get();
}
public Iterable<CardAreaPanel> getCardPanels() {
return cardPanels();
return cardPanels.get();
}
@Override
public int getCount() {
return cardPanels().size();
return cardPanels.get().size();
}
@Override
@@ -85,15 +61,15 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
for (CardView card : model) {
CardAreaPanel cardPanel = CardAreaPanel.get(card);
addCardPanelToDisplayArea(cardPanel);
cardPanels().add(cardPanel);
if (newCardPanel == null && !orderedCards().contains(card)) {
cardPanels.get().add(cardPanel);
if (newCardPanel == null && !orderedCards.get().contains(card)) {
newCardPanel = cardPanel;
}
}
}
if (isVisible()) { //only revalidate if currently visible
revalidate();
if (newCardPanel != null) { //if new cards added, ensure first new card is scrolled into view
scrollIntoView(newCardPanel);
}
@@ -105,7 +81,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
if (isVisible() == b0) { return; }
super.setVisible(b0);
if (b0) { //when zone becomes visible, ensure display area of panels is updated and panels layed out
for (CardAreaPanel pnl : cardPanels()) {
for (CardAreaPanel pnl : cardPanels.get()) {
pnl.displayArea = this;
}
revalidate();
@@ -140,7 +116,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
CardPanelContainer.this.remove(CardPanel.getDragAnimationPanel());
CardPanelContainer.this.setMouseDragPanel(null);
}*/
cardPanels().remove(fromPanel);
cardPanels.get().remove(fromPanel);
remove(fromPanel);
}
@@ -151,14 +127,14 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
@Override
public void clear() {
super.clear();
if (!cardPanels().isEmpty()) {
for (CardAreaPanel panel : cardPanels()) {
if (!cardPanels.get().isEmpty()) {
for (CardAreaPanel panel : cardPanels.get()) {
if (panel.displayArea == null || panel.displayArea == this ||
!panel.displayArea.cardPanels().contains(panel)) { //don't reset if panel's displayed in another area already
!panel.displayArea.cardPanels.get().contains(panel)) { //don't reset if panel's displayed in another area already
panel.reset();
}
}
cardPanels().clear();
cardPanels.get().clear();
}
}
@@ -176,7 +152,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
}
}
orderedCards().add(cardPanel.getCard());
orderedCards.get().add(cardPanel.getCard());
cardPanel.setBounds(x, y, cardWidth, cardHeight);
if (cardPanel.getNextPanelInStack() != null) { //add next panel in stack if needed
@@ -195,14 +171,14 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
@Override
protected ScrollBounds layoutAndGetScrollBounds(float visibleWidth, float visibleHeight) {
orderedCards().clear();
orderedCards.get().clear();
float x = 0;
float y = 0;
float cardHeight = visibleHeight;
float cardWidth = getCardWidth(cardHeight);
for (CardAreaPanel cardPanel : new ArrayList<>(cardPanels())) {
for (CardAreaPanel cardPanel : new ArrayList<>(cardPanels.get())) {
if (cardPanel != null) {
int count = addCards(cardPanel, x, y, cardWidth, cardHeight);
x += cardWidth + (count - 1) * cardWidth * CARD_STACK_OFFSET;
@@ -223,8 +199,8 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
public String getActivateAction(int index) {
if(!GuiBase.isNetworkplay()) {
//causes lag on netplay client side, also index shouldn't be out of bounds
if (index >= 0 && index < orderedCards().size())
return MatchController.instance.getGameController().getActivateDescription(orderedCards().get(index));
if (index >= 0 && index < orderedCards.get().size())
return MatchController.instance.getGameController().getActivateDescription(orderedCards.get().get(index));
}
return Forge.getLocalizer().getMessage("lblActivateAction"); //simple text on card zoom swipe up
@@ -233,15 +209,15 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
@Override
public void setSelectedIndex(int index) {
//just scroll card into view
if (index < orderedCards().size()) {
final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards().get(index));
if (index < orderedCards.get().size()) {
final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards.get().get(index));
scrollIntoView(cardPanel);
}
}
@Override
public void activate(int index) {
final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards().get(index));
final CardAreaPanel cardPanel = CardAreaPanel.get(orderedCards.get().get(index));
//must invoke in game thread in case a dialog needs to be shown
ThreadUtil.invokeInGameThread(() -> cardPanel.selectCard(false));
}
@@ -449,7 +425,7 @@ public abstract class VCardDisplayArea extends VDisplayArea implements ActivateH
public void showZoom() {
if (displayArea == null) { return; }
final List<CardView> cards = displayArea.orderedCards();
final List<CardView> cards = displayArea.orderedCards.get();
CardZoom.show(cards, cards.indexOf(getCard()), displayArea);
}

View File

@@ -615,7 +615,13 @@ public class VPlayerPanel extends FContainer {
private final VDisplayArea displayArea;
private InfoTab(FSkinImageInterface icon0, VDisplayArea displayArea0) {
icon = icon0;
// missing or invalid player infotab icon probably old theme or custom theme.
if (icon0 == null) {
System.err.println("Missing/Invalid VPlayerPanel icon for: " + displayArea0 + " , defaulting to blank icon. Check your theme/skin layout.");
icon = FSkinImage.BLANK;
} else {
icon = icon0;
}
displayArea = displayArea0;
}

View File

@@ -33,6 +33,7 @@ import forge.menu.FMenuTab;
import forge.menu.FPopupMenu;
import forge.player.PlayerZoneUpdates;
import forge.screens.match.MatchController;
import forge.screens.match.MatchScreen;
import forge.screens.match.TargetingOverlay;
import forge.toolbox.FCardPanel;
import forge.toolbox.FDisplayObject;
@@ -179,7 +180,8 @@ public class VStack extends FDropDown {
activeItem = display;
}
else {
activeItem.setHeight(display.preferredHeight); //increase active item height to preferred height if needed
if (display != null)
activeItem.setHeight(display.preferredHeight); //increase active item height to preferred height if needed
if (activeItem.getBottom() > y) {
y = activeItem.getBottom(); //ensure stack height increases if needed
}
@@ -233,7 +235,7 @@ public class VStack extends FDropDown {
}
for (PlayerView p : instance.getTargetPlayers()) {
TargetingOverlay.ArcConnection conn = activator.isOpponentOf(p) ? TargetingOverlay.ArcConnection.FoesStackTargeting : TargetingOverlay.ArcConnection.FriendsStackTargeting;
TargetingOverlay.drawArrow(g, arrowOrigin, MatchController.getView().getPlayerPanel(p).getAvatar().getTargetingArrowOrigin(), conn);
TargetingOverlay.drawArrow(g, arrowOrigin, MatchScreen.getPlayerPanel(p).getAvatar().getTargetingArrowOrigin(), conn);
}
instance = instance.getSubInstance();
}

View File

@@ -37,11 +37,11 @@ public class VZoneDisplay extends VCardDisplayArea {
float y = screenToLocalY(screenY);
if (revealedPanel.contains(x, y)) { return; }
int idx = cardPanels().size() - 1;
int idx = cardPanels.get().size() - 1;
for (int i = getChildCount() - 2; i >= 0; i--) {
final FDisplayObject cardPanel = getChildAt(i);
if (cardPanel.contains(x, y)) {
idx = cardPanels().indexOf(cardPanel);
idx = cardPanels.get().indexOf(cardPanel);
break;
}
}
@@ -54,9 +54,9 @@ public class VZoneDisplay extends VCardDisplayArea {
if (revealedPanel == null) { //if no overlapping panels, just pan scroll as normal
return super.pan(x, y, deltaX, deltaY, moreVertical);
}
int idx = cardPanels().size() - 1;
int idx = cardPanels.get().size() - 1;
for (int i = idx - 1; i >= 0; i--) {
if (cardPanels().get(i).contains(x, y)) {
if (cardPanels.get().get(i).contains(x, y)) {
idx = i;
break;
}
@@ -66,15 +66,15 @@ public class VZoneDisplay extends VCardDisplayArea {
}
private void setRevealedPanel(int idx) {
if (idx >= 0 && idx < cardPanels().size())
revealedPanel = cardPanels().get(idx);
if (idx >= 0 && idx < cardPanels.get().size())
revealedPanel = cardPanels.get().get(idx);
else
return;
clearChildren();
if (Forge.isLandscapeMode()) {
//for landscape mode, just show revealed card on top
for (CardAreaPanel cardPanel : cardPanels()) {
for (CardAreaPanel cardPanel : cardPanels.get()) {
if (cardPanel != revealedPanel) {
add(cardPanel);
}
@@ -82,16 +82,16 @@ public class VZoneDisplay extends VCardDisplayArea {
}
else {
//for portrait mode, cascade cards back from revealed panel
int maxIdx = cardPanels().size() - 1;
int maxIdx = cardPanels.get().size() - 1;
int offset = Math.max(idx, maxIdx - idx);
for (int i = offset; i > 0; i--) {
int idx1 = idx - i;
int idx2 = idx + i;
if (idx1 >= 0) {
add(cardPanels().get(idx1));
add(cardPanels.get().get(idx1));
}
if (idx2 <= maxIdx) {
add(cardPanels().get(idx2));
add(cardPanels.get().get(idx2));
}
}
}
@@ -122,7 +122,7 @@ public class VZoneDisplay extends VCardDisplayArea {
return new ScrollBounds(visibleWidth, visibleHeight);
}
orderedCards().clear();
orderedCards.get().clear();
if (Forge.isLandscapeMode() && layoutVerticallyForLandscapeMode()) {
return layoutAndGetScrollBoundsLandscape(visibleWidth, visibleHeight);
@@ -134,22 +134,22 @@ public class VZoneDisplay extends VCardDisplayArea {
float cardWidth = getCardWidth(cardHeight);
float dx = cardWidth;
float totalWidth = cardWidth * cardPanels().size();
float totalWidth = cardWidth * cardPanels.get().size();
if (totalWidth > visibleWidth && totalWidth <= visibleWidth * 2) {
//allow overlapping cards up to one half of the card,
//otherwise don't overlap and allow scrolling horizontally
dx *= (visibleWidth - cardWidth) / (totalWidth - cardWidth);
dx += FCardPanel.PADDING / cardPanels().size(); //make final card go right up to right edge of screen
dx += FCardPanel.PADDING / cardPanels.get().size(); //make final card go right up to right edge of screen
if (revealedPanel == null) {
revealedPanel = cardPanels().get(cardPanels().size() - 1);
revealedPanel = cardPanels.get().get(cardPanels.get().size() - 1);
}
}
else {
revealedPanel = null;
}
for (CardAreaPanel cardPanel : cardPanels()) {
orderedCards().add(cardPanel.getCard());
for (CardAreaPanel cardPanel : cardPanels.get()) {
orderedCards.get().add(cardPanel.getCard());
cardPanel.setBounds(x, y, cardWidth, cardHeight);
x += dx;
}
@@ -165,7 +165,7 @@ public class VZoneDisplay extends VCardDisplayArea {
float dy = cardHeight;
float scrollHeight;
int rowCount = (int)Math.ceil((float)cardPanels().size() / 2f);
int rowCount = (int)Math.ceil((float)cardPanels.get().size() / 2f);
float totalHeight = cardHeight * rowCount;
if (totalHeight > visibleHeight && totalHeight <= visibleHeight * 3) {
//allow overlapping cards up to one third of the card,
@@ -173,7 +173,7 @@ public class VZoneDisplay extends VCardDisplayArea {
dy *= (visibleHeight - cardHeight) / (totalHeight - cardHeight);
dy += FCardPanel.PADDING / rowCount; //make final card go right up to right edge of screen
if (revealedPanel == null) {
revealedPanel = cardPanels().get(cardPanels().size() - 1);
revealedPanel = cardPanels.get().get(cardPanels.get().size() - 1);
}
scrollHeight = visibleHeight;
}
@@ -182,10 +182,10 @@ public class VZoneDisplay extends VCardDisplayArea {
scrollHeight = rowCount * dy;
}
for (CardAreaPanel cardPanel : cardPanels()) {
orderedCards().add(cardPanel.getCard());
for (CardAreaPanel cardPanel : cardPanels.get()) {
orderedCards.get().add(cardPanel.getCard());
cardPanel.setBounds(x, y, cardWidth, cardHeight);
if (orderedCards().size() % 2 == 0) {
if (orderedCards.get().size() % 2 == 0) {
x = 0;
y += dy;
}

View File

@@ -244,6 +244,10 @@ public class SettingsPage extends TabPage<SettingsScreen> {
Forge.getLocalizer().getMessage("cbExperimentalRestore"),
Forge.getLocalizer().getMessage("nlExperimentalRestore")),
1);
lstSettings.addItem(new CustomSelectSetting(FPref.MATCH_AI_TIMEOUT, Forge.getLocalizer().getMessage("cbAITimeout"),
Forge.getLocalizer().getMessage("nlAITimeout"),
Lists.newArrayList("5", "10", "60", "120", "240", "300", "600")),
1);
lstSettings.addItem(new BooleanSetting(FPref.FILTERED_HANDS,
Forge.getLocalizer().getMessage("cbFilteredHands"),
Forge.getLocalizer().getMessage("nlFilteredHands")),