From 16f3958ba5affa8d209202bb393d01e3f6458e7d Mon Sep 17 00:00:00 2001 From: Jetz Date: Wed, 4 Feb 2026 08:58:48 -0500 Subject: [PATCH 1/6] Fix NPE on draw. --- forge-gui/src/main/java/forge/sound/EventVisualizer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/forge-gui/src/main/java/forge/sound/EventVisualizer.java b/forge-gui/src/main/java/forge/sound/EventVisualizer.java index c2623ee0aca..e5bc262d358 100644 --- a/forge-gui/src/main/java/forge/sound/EventVisualizer.java +++ b/forge-gui/src/main/java/forge/sound/EventVisualizer.java @@ -14,6 +14,7 @@ import forge.util.maps.MapOfLists; import java.util.Collection; +import java.util.Objects; /** * This class is in charge of converting any forge.game.event.Event to a SoundEffectType. @@ -115,7 +116,7 @@ public SoundEffectType visit(final GameEventBlockersDeclared event) { */ @Override public SoundEffectType visit(final GameEventGameOutcome event) { - final boolean humanWonTheDuel = event.result().getWinningLobbyPlayer().equals(player); + final boolean humanWonTheDuel = Objects.equals(event.result().getWinningLobbyPlayer(), player); return humanWonTheDuel ? SoundEffectType.WinDuel : SoundEffectType.LoseDuel; } From d904732a447fb5c01c14bea83b4b8489ea2ab8b2 Mon Sep 17 00:00:00 2001 From: Jetz Date: Wed, 4 Feb 2026 09:44:29 -0500 Subject: [PATCH 2/6] Handle ante during Adventure events --- forge-core/src/main/java/forge/deck/Deck.java | 16 ++++++++++++++++ .../src/forge/adventure/scene/DuelScene.java | 19 ++++++++++++++----- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/forge-core/src/main/java/forge/deck/Deck.java b/forge-core/src/main/java/forge/deck/Deck.java index 22f11d22c8f..63e2051f076 100644 --- a/forge-core/src/main/java/forge/deck/Deck.java +++ b/forge-core/src/main/java/forge/deck/Deck.java @@ -198,6 +198,22 @@ public PaperCard removeCardName(String name) { return null; } + /** + * Removes a card from any section it's found in, prioritizing the sideboard over other sections. + */ + public void removeAnteCard(PaperCard card) { + if (has(DeckSection.Sideboard) && get(DeckSection.Sideboard).contains(card)) { + get(DeckSection.Sideboard).remove(card); + return; + } + for (CardPool pool : parts.values()) { + if(pool.contains(card)) { + pool.remove(card); + return; + } + } + } + // will return new if it was absent public CardPool getOrCreate(DeckSection deckSection) { CardPool p = get(deckSection); diff --git a/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java b/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java index 0db85a5997b..412661fcd0e 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java @@ -113,12 +113,21 @@ public void GameEnd() { // Mostly for ante handling, but also blacker lotus GameOutcome.AnteResult anteResult = hostedMatch.getGame().getOutcome().getAnteResult(humanPlayer); if (anteResult != null) { - for (PaperCard card : anteResult.wonCards) { - Current.player().addCard(card); + if (eventData != null) { + //In an event. Apply the ante result to the current event deck. + eventData.registeredDeck.getOrCreate(DeckSection.Sideboard).add(anteResult.wonCards); + for(PaperCard card : anteResult.lostCards) + eventData.registeredDeck.removeAnteCard(card); + //Could also add the cards to the opponent's pool, but their games aren't simulated and they never edit their decks. } - for (PaperCard card : anteResult.lostCards) { - // We could clean this up by trying to combine all the lostCards into a mapping, but good enough for now - Current.player().removeLostCardFromPools(card); + else { + for (PaperCard card : anteResult.wonCards) { + Current.player().addCard(card); + } + for (PaperCard card : anteResult.lostCards) { + // We could clean this up by trying to combine all the lostCards into a mapping, but good enough for now + Current.player().removeLostCardFromPools(card); + } } } } catch (Exception e) { From 55c85eac0404902612fdf9d828350d949a7eb122 Mon Sep 17 00:00:00 2001 From: Jetz Date: Tue, 10 Feb 2026 09:55:17 -0500 Subject: [PATCH 3/6] Handle ante across matches with multiple games --- .../src/main/java/forge/game/GameOutcome.java | 14 ++++++++++++-- forge-game/src/main/java/forge/game/Match.java | 10 ++++++++++ .../src/forge/adventure/scene/DuelScene.java | 7 +++++-- .../java/forge/gamemodes/match/HostedMatch.java | 4 ++++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameOutcome.java b/forge-game/src/main/java/forge/game/GameOutcome.java index 875d5718b8d..41144102659 100644 --- a/forge-game/src/main/java/forge/game/GameOutcome.java +++ b/forge-game/src/main/java/forge/game/GameOutcome.java @@ -58,11 +58,21 @@ public AnteResult() { } public void addWon(List cards) { - this.wonCards.addAll(cards); + for(PaperCard c : cards) { + if(lostCards.contains(c)) + lostCards.remove(c); + else + wonCards.add(c); + } } public void addLost(List cards) { - this.lostCards.addAll(cards); + for(PaperCard c : cards) { + if(wonCards.contains(c)) + wonCards.remove(c); + else + lostCards.add(c); + } } } diff --git a/forge-game/src/main/java/forge/game/Match.java b/forge-game/src/main/java/forge/game/Match.java index a48a8785f79..3320c5c8b70 100644 --- a/forge-game/src/main/java/forge/game/Match.java +++ b/forge-game/src/main/java/forge/game/Match.java @@ -445,6 +445,16 @@ private void executeOwnershipChanges(Game lastGame) { } } + public GameOutcome.AnteResult getAnteResult(RegisteredPlayer player) { + GameOutcome.AnteResult out = new GameOutcome.AnteResult(); + for (GameOutcome outcome : gameOutcomes.values()) { + GameOutcome.AnteResult gameAnte = outcome.getAnteResult(player); + out.addWon(gameAnte.wonCards); + out.addLost(gameAnte.lostCards); + } + return out; + } + /** * Fire only the events after they became real for gamestate and won't get replaced.
* The events are sent to UI, log and sound system. Network listeners are under development. diff --git a/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java b/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java index 412661fcd0e..cc6f3f94682 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java @@ -111,13 +111,16 @@ public void GameEnd() { } // Mostly for ante handling, but also blacker lotus - GameOutcome.AnteResult anteResult = hostedMatch.getGame().getOutcome().getAnteResult(humanPlayer); + GameOutcome.AnteResult anteResult = hostedMatch.getAnteResult(humanPlayer); if (anteResult != null) { if (eventData != null) { //In an event. Apply the ante result to the current event deck. eventData.registeredDeck.getOrCreate(DeckSection.Sideboard).add(anteResult.wonCards); - for(PaperCard card : anteResult.lostCards) + eventData.draftedDeck.getOrCreate(DeckSection.Sideboard).add(anteResult.wonCards); + for(PaperCard card : anteResult.lostCards) { eventData.registeredDeck.removeAnteCard(card); + eventData.draftedDeck.removeAnteCard(card); + } //Could also add the cards to the opponent's pool, but their games aren't simulated and they never edit their decks. } else { diff --git a/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java b/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java index 85f6db690e3..5955647e8c5 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/HostedMatch.java @@ -355,6 +355,10 @@ public boolean isMatchOver() { return isMatchOver; } + public GameOutcome.AnteResult getAnteResult(RegisteredPlayer player) { + return match.getAnteResult(player); + } + private final class MatchUiEventVisitor extends IGameEventVisitor.Base implements IUiEventVisitor { @Override public Void visit(final UiEventBlockerAssigned event) { From 695382ddce85107c0d9de3439eca5a83c3be00a0 Mon Sep 17 00:00:00 2001 From: Jetz Date: Tue, 10 Feb 2026 09:59:46 -0500 Subject: [PATCH 4/6] Support arrow keys and page up/down in item manager. --- forge-gui-mobile/src/forge/Forge.java | 35 +++++++++---------- .../src/forge/itemmanager/ItemManager.java | 4 ++- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/forge-gui-mobile/src/forge/Forge.java b/forge-gui-mobile/src/forge/Forge.java index 8d1448e197d..c241530fbba 100644 --- a/forge-gui-mobile/src/forge/Forge.java +++ b/forge-gui-mobile/src/forge/Forge.java @@ -1196,24 +1196,10 @@ public boolean keyDown(int keyCode) { shiftKeyDown = true; } - // Cursor keys emulate swipe gestures - // First we touch the screen and later swipe (fling) in the direction of the key pressed - if (keyCode == Keys.LEFT) { - touchDown(0, 0, 0, 0); - return fling(1000, 0); - } - if (keyCode == Keys.RIGHT) { - touchDown(0, 0, 0, 0); - return fling(-1000, 0); - } - if (keyCode == Keys.UP) { - touchDown(0, 0, 0, 0); - return fling(0, -1000); - } - if (keyCode == Keys.DOWN) { - touchDown(0, 0, 0, 0); - return fling(0, 1000); - } + if (keyCode == Keys.LEFT || keyCode == Keys.RIGHT || keyCode == Keys.UP || keyCode == Keys.DOWN) + if(emulateSwipe(keyCode)) + return true; + if (keyCode == Keys.BACK) { if ((destroyThis && !isMobileAdventureMode) || (splashScreen != null && splashScreen.isShowModeSelector())) exitAnimation(false); @@ -1237,6 +1223,19 @@ else if (onHomeScreen() && isLandscapeMode()) return keyInputAdapter.keyDown(keyCode); } + private boolean emulateSwipe (int keyCode) { + // Cursor keys emulate swipe gestures + // First we touch the screen and later swipe (fling) in the direction of the key pressed + touchDown(0, 0, 0, 0); + return switch (keyCode) { + case Keys.LEFT -> fling(1000, 0); + case Keys.RIGHT -> fling(-1000, 0); + case Keys.UP -> fling(0, -1000); + case Keys.DOWN -> fling(0, 1000); + default -> false; + }; + } + @Override public boolean keyUp(int keyCode) { keyTyped = false; //reset on keyUp diff --git a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java index f2ed5fee7ad..755a801afd2 100644 --- a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java +++ b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java @@ -1238,10 +1238,12 @@ public boolean keyDown(int keyCode) { boolean usingListView = currentView == listView; switch(keyCode) { - case(Input.Keys.DPAD_RIGHT): + case Input.Keys.DPAD_RIGHT: + case Input.Keys.PAGE_DOWN: setSelectedIndexRelative(usingListView ? 10 : 1); return true; case Input.Keys.DPAD_LEFT: + case Input.Keys.PAGE_UP: setSelectedIndexRelative(usingListView ? -10 : -1); return true; case Input.Keys.DPAD_DOWN: From f64eb3e5dcc0aaa580e5f8d1304c24e507f9c091 Mon Sep 17 00:00:00 2001 From: Jetz72 Date: Tue, 10 Feb 2026 10:25:32 -0500 Subject: [PATCH 5/6] Prevent potential NPE with Jumpstart Ante --- forge-gui-mobile/src/forge/adventure/scene/DuelScene.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java b/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java index cc6f3f94682..4e9e6d7ec65 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/DuelScene.java @@ -116,10 +116,12 @@ public void GameEnd() { if (eventData != null) { //In an event. Apply the ante result to the current event deck. eventData.registeredDeck.getOrCreate(DeckSection.Sideboard).add(anteResult.wonCards); - eventData.draftedDeck.getOrCreate(DeckSection.Sideboard).add(anteResult.wonCards); + if(eventData.draftedDeck != null) + eventData.draftedDeck.getOrCreate(DeckSection.Sideboard).add(anteResult.wonCards); for(PaperCard card : anteResult.lostCards) { eventData.registeredDeck.removeAnteCard(card); - eventData.draftedDeck.removeAnteCard(card); + if(eventData.draftedDeck != null) + eventData.draftedDeck.removeAnteCard(card); } //Could also add the cards to the opponent's pool, but their games aren't simulated and they never edit their decks. } From 8e8a1bd69738e5030e8d76dd6b544dbc0744c931 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Fri, 13 Feb 2026 16:53:22 +0100 Subject: [PATCH 6/6] fix import --- forge-gui/src/main/java/forge/sound/EventVisualizer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/forge-gui/src/main/java/forge/sound/EventVisualizer.java b/forge-gui/src/main/java/forge/sound/EventVisualizer.java index 32bb2a539cc..aba7eb5defa 100644 --- a/forge-gui/src/main/java/forge/sound/EventVisualizer.java +++ b/forge-gui/src/main/java/forge/sound/EventVisualizer.java @@ -12,7 +12,6 @@ import forge.gui.events.UiEventNextGameDecision; import forge.util.TextUtil; -import java.util.Collection; import java.util.Objects; import com.google.common.collect.Multimap;