diff --git a/forge-game/src/main/java/forge/game/player/PlayerFactoryUtil.java b/forge-game/src/main/java/forge/game/player/PlayerFactoryUtil.java index 34933815924..4876d433533 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/player/PlayerFactoryUtil.java @@ -2,6 +2,7 @@ import forge.game.card.Card; import forge.game.card.CardFactoryUtil; +import forge.game.keyword.Hexproof; import forge.game.keyword.KeywordInterface; import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementHandler; @@ -12,23 +13,28 @@ public class PlayerFactoryUtil { public static void addStaticAbility(final KeywordInterface inst, final Player player) { String keyword = inst.getOriginal(); - if (keyword.startsWith("Hexproof")) { - final StringBuilder sbDesc = new StringBuilder("Hexproof"); + if (keyword.startsWith("Hexproof") + && inst instanceof Hexproof hexproof) { final StringBuilder sbValid = new StringBuilder(); - if (!keyword.equals("Hexproof")) { - final String[] k = keyword.split(":"); - - sbDesc.append(" from ").append(k[2]); - sbValid.append("| ValidSource$ ").append(k[1]); + if (!hexproof.getValidType().isEmpty()) { + sbValid.append("| ValidSource$ ") + .append(hexproof.getValidType()); } - String effect = "Mode$ CantTarget | ValidTarget$ Player.You | Secondary$ True " - + sbValid.toString() + " | Activator$ Opponent | EffectZone$ Command | Description$ " - + sbDesc.toString() + " (" + inst.getReminderText() + ")"; + String effect = "Mode$ CantTarget" + + " | ValidTarget$ Player.You" + + " | Secondary$ True " + + sbValid + + " | Activator$ Opponent" + + " | EffectZone$ Command" + + " | Description$ " + + inst.getTitle() + + " (" + inst.getReminderText() + ")"; final Card card = player.getKeywordCard(); - inst.addStaticAbility(StaticAbility.create(effect, card, card.getCurrentState(), false)); + inst.addStaticAbility(StaticAbility.create( + effect, card, card.getCurrentState(), false)); } else if (keyword.equals("Shroud")) { String effect = "Mode$ CantTarget | ValidTarget$ Player.You | Secondary$ True " + "| EffectZone$ Command | Description$ Shroud (" + inst.getReminderText() + ")"; diff --git a/forge-gui-mobile/src/forge/adventure/player/AdventurePlayer.java b/forge-gui-mobile/src/forge/adventure/player/AdventurePlayer.java index c7cfba8e1bf..afd1d2d5a2f 100644 --- a/forge-gui-mobile/src/forge/adventure/player/AdventurePlayer.java +++ b/forge-gui-mobile/src/forge/adventure/player/AdventurePlayer.java @@ -1284,6 +1284,29 @@ public int cardSellPrice(PaperCard card) { return (int) (basePrice * (2.0f - townPriceModifier)); } + /** + * Adventure card shop buy price (same formula as {@link forge.adventure.scene.RewardScene}). + * + * @param locationPriceModifier town × shop reputation factor from the current shop + */ + public int cardShopBuyPrice(PaperCard card, float locationPriceModifier) { + if (card == null || card.hasNoSellValue()) { + return 0; + } + int price = CardUtil.getCardPrice(card); + price *= goldModifier(); + price *= locationPriceModifier; + return Math.max(0, price); + } + + /** + * Deck import buy-missing: same as a card shop in a town with no reputation (modifier 1.0), + * including the player's equipment/blessing shop discounts via {@link #goldModifier()}. + */ + public int cardBuyPrice(PaperCard card) { + return cardShopBuyPrice(card, 1f); + } + /** * Sells a number of copies of a card. * @return the number of copies successfully sold. @@ -1587,6 +1610,40 @@ public int copyDeck() { return -1; } + /** + * Finds the first empty deck slot, expanding if needed. + * Returns -1 only if all 99 slots are occupied. + */ + public int findFirstEmptySlot() { + for (int i = 0; i < maxDeckCount; i++) { + if (i >= getDeckCount()) addDeck(); + if (isEmptyDeck(i)) return i; + } + if (getDeckCount() < 99) { + maxDeckCount = Math.min(maxDeckCount + 1, 99); + addDeck(); + return getDeckCount() - 1; + } + return -1; + } + + /** + * Replaces the contents of a deck slot with an imported deck. + * Clears all existing sections and copies from the source. + */ + public void importIntoSlot(int slot, Deck importedDeck) { + Deck target = getDeck(slot); + for (DeckSection section : DeckSection.values()) { + if (target.has(section)) target.get(section).clear(); + } + for (java.util.Map.Entry entry : importedDeck) { + target.getOrCreate(entry.getKey()).addAll(entry.getValue()); + } + if (importedDeck.getName() != null && !importedDeck.getName().isEmpty()) { + target.setName(importedDeck.getName()); + } + } + private void ensureDeckLoadoutsSize() { while (deckLoadouts.size() < getDeckCount()) { deckLoadouts.add(null); diff --git a/forge-gui-mobile/src/forge/adventure/scene/DeckSelectScene.java b/forge-gui-mobile/src/forge/adventure/scene/DeckSelectScene.java index d2dfad086c0..6671398acf8 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/DeckSelectScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/DeckSelectScene.java @@ -1,19 +1,29 @@ package forge.adventure.scene; +import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.ui.*; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.IntMap; +import com.badlogic.gdx.utils.Timer; import com.github.tommyettinger.textra.TextraButton; import com.github.tommyettinger.textra.TextraLabel; import forge.Forge; import forge.adventure.player.AdventurePlayer; import forge.adventure.stage.GameHUD; +import forge.adventure.util.AdventureDialogUtil; +import forge.adventure.util.AdventureFilePicker; +import forge.adventure.util.CardUtil; import forge.adventure.util.Controls; import forge.adventure.util.Current; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + public class DeckSelectScene extends UIScene { private final IntMap buttons = new IntMap<>(); private final IntMap