Skip to content

Route opponent-hand reveal and discard through FloatingZone on desktop#10660

Open
MostCromulent wants to merge 5 commits into
Card-Forge:masterfrom
MostCromulent:opponent-discard-hand-click
Open

Route opponent-hand reveal and discard through FloatingZone on desktop#10660
MostCromulent wants to merge 5 commits into
Card-Forge:masterfrom
MostCromulent:opponent-discard-hand-click

Conversation

@MostCromulent
Copy link
Copy Markdown
Contributor

@MostCromulent MostCromulent commented May 11, 2026

USING MIND WARP, X=2

BEFORE:
Reveal step
Screenshot 2026-05-12 074617
Select step
Screenshot 2026-05-12 074624

AFTER:
Reveal step
Screenshot 2026-05-12 074032
Select step
Screenshot 2026-05-12 074044


Summary

Per #10646 (comment): RevealYouChoose / LookYouChoose discards (Inquisition, Mind Warp, etc.) currently surface as a names-list reveal dialog followed by a separate selection grid, when the opponent's hand could be the selection UI directly.

This routes both steps through the existing FloatingZone: reveal opens the hand face-up with a minimal OK acknowledgement; the pick uses InputSelectCardsFromList so legal targets are clicked in that same FloatingZone.

Implementation

  • PlayerControllerHuman.reveal() — desktop opponent-hand reveals swap getGui().reveal() (names list) for tempShowZones + getGui().message() (visual hand + minimal OK).
  • PlayerControllerHuman.chooseCardsToDiscardFrom() — opponent-discard pick routes through InputSelectCardsFromList; tempShows the full hand for Reveal*/Look* modes without RevealNumber so revealed non-selectable cards stay visible. RevealNumber spells (Blackmail, Cabal Interrogator) keep the conservative tempShow-of-valid path so unrevealed cards stay face-down.
  • CMatchUI.hideZones() — fix a pre-existing asymmetry: tempShowZones special-cases ZoneType.Hand, but hideZones only checked FLOATING_ZONE_TYPES (which omits Hand). The FloatingZone opens for an opponent's hand but never closed programmatically.

Safety

Both PCH changes gate on UI_SELECT_FROM_CARD_DISPLAYS && !getGui().isLibgdxPort() — the same condition useSelectCardsInput already uses. Mobile and preference-off paths are unchanged. isLibgdxPort() is the per-GUI network-safe check from #10615. No engine, protocol, or TrackableProperty changes.

RevealNumber partial-reveal spells (e.g. Blackmail revealing 3 of 5) are rendered safely: FloatingZone.getCards() returns all hand cards, but each is drawn according to the existing mayView check — only mayLookTemp-flagged cards (the revealed subset) show their faces, the rest render as card backs. Non-revealed cards' identities stay hidden; only hand size is conveyed, which is already public info.

Potential code cleanup (not in this PR)

FPref.UI_SELECT_FROM_CARD_DISPLAYS was added in 2019 (commit 09fc3ae60c2) alongside the original arrangeForScry GUI introduction, which brought in-zone selection as a new UX. The preference has defaulted to true with no user-facing UI toggle ever since — effectively the active desktop UI path for 7+ years.

The preference gates whether InputSelect extends beyond the local player's own hand and battlefield to other zones (opponent-hand FloatingZone, libraries, graveyards, etc.) — i.e. click a card in its zone display versus fall back to a names-list dialog. All three desktop call sites in PlayerControllerHuman pair it with !getGui().isLibgdxPort().

A separate cleanup PR could drop the FPref entry, simplify the three dual-gate sites to single !isLibgdxPort() checks, and collapse the now-dead fallback branches in useSelectCardsInput (the zone-set ternary) and arrangeForScry (the manual-order path).


🤖 Generated with Claude Code

…on desktop

For RevealYouChoose / LookYouChoose discards (Inquisition, Thoughtseize, etc.)
on desktop, present the opponent's hand visually in the FloatingZone instead
of a names-list dialog:

- Reveal step: replace getGui().reveal() (names list) with tempShowZones plus
  a minimal OK dialog. The actual cards are visible in the FloatingZone; the
  dialog just blocks until the user acknowledges.
- Pick step: route through InputSelectCardsFromList so the user clicks legal
  targets directly in the FloatingZone. tempShow the full hand for Reveal/Look
  modes without RevealNumber so non-selectable revealed cards remain visible;
  for RevealNumber spells (Blackmail, Cabal Interrogator, etc.) the path is
  unchanged so unrevealed cards stay hidden.

Both halves gate on UI_SELECT_FROM_CARD_DISPLAYS && !isLibgdxPort() -- the
same condition useSelectCardsInput already uses. Mobile and the
preference-off path keep the existing dialog flow.

Also fix CMatchUI.hideZones to mirror tempShowZones' Hand handling -- the
show side opens the FloatingZone for an opponent's Hand, but the hide side
only checked FLOATING_ZONE_TYPES (which omits Hand), so the FloatingZone
never closed programmatically after the prompt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@MostCromulent
Copy link
Copy Markdown
Contributor Author

MostCromulent commented May 11, 2026

Let me know if you want the FPref.UI_SELECT_FROM_CARD_DISPLAYS code cleanup suggested above as part of this PR.

@tool4ever
Copy link
Copy Markdown
Contributor

tool4ever commented May 12, 2026

I think in general this seems better:

  • now leverages the advantage of being able to inspect the boardstate, helping you make tough decisions
  • the used dialog had incorrect implications about the order mattering (while rare it sometimes does but that decision is done afterwards by the owner!)

the only minor downside remaining is no longer being able to use the keyboard to progress:

  • though arguably that was a rather fiddly process, I doubt many users were doing that over mouse unless they had a very good reason (technical/physical)...?
  • but I think we could do much better and turn it into a real speed-up for everyone across multiple use cases 🧠

what if we (temporarily) assign the number keys to the first 9 selectables? might have to draw small labels on them for clearity or something - maybe based on Ctrl press 🤔
want to give that a shot? 🥳

(there's actually one place in CMatchUi that uses VK_* similarly already but for abilities)

@MostCromulent
Copy link
Copy Markdown
Contributor Author

what if we (temporarily) assign the number keys to the first 9 selectables? might have to draw small labels on them for clearity or something - maybe based on Ctrl press 🤔
want to give that a shot? 🥳

yeah I'll try that out - might also look at how seamlessly we could integrate a search filter at the top as well, since we also lose that compared being able to start typing in the previous list view

Adds a name-search field at the top of FloatingZone and Ctrl+1-9
hotkeys for the first nine selectable cards during an active
selection prompt. Numbered badges render on those cards and a small
hint footer surfaces the shortcut. Also fixes CMatchUI.updateCards
so opponent-hand FloatingZones refresh on highlight changes —
previously only the local VHand was covered.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@MostCromulent
Copy link
Copy Markdown
Contributor Author

MostCromulent commented May 12, 2026

Screenshot 2026-05-12 213043

Should work for any card-select prompt that uses FloatingZone - opponent hand, graveyard, exile, library etc.

Does not apply for cases which don't use FloatingZone - own hand, battlefield, players, ephemeral non-zone cards (though as follow up I'm going to explore what we could do about the ephemerals; plausibly could generate a temp FloatingZone window containing the ephemeral cards to take advantage of same UX).

if (!fz.isVisible()) continue;
final CardPanel target = fz.findPanelByHotkeyDigit(digit);
if (target == null) continue;
fz.getMatchUI().getGameController().selectCard(target.getCard(), null,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could use 0 to just select the first X minimum needed to proceed
but might be too tricky to obtain here...?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this shouldn't be too hard but would require small protocol change.

Currently InputSelectCardsFromList doesn't broadcast the min/max amount to network clients, it just waits to receive the choice. Would need to add that to the protocol. This is what happens with list dialogs already so there's precedent for it.

Changes involved:

  1. IGuiGame.java — new interface method
  void setSelectionRange(int min, int max);
  Placed next to setSelectables(...).

  2. AbstractGuiGame.java — base impl (all concrete GUIs inherit it: CMatchUI, MatchController mobile, RemoteClientGuiGame,
  HeadlessNetworkGuiGame)
  private int selectionMin = 0, selectionMax = 0;

  public void setSelectionRange(int min, int max) { selectionMin = min; selectionMax = max; }
  public int getSelectionMin() { return selectionMin; }

  // inside clearSelectables():
  selectionMin = 0; selectionMax = 0;

  3. ProtocolMethod.java — register for the wire
  setSelectionRange (Mode.SERVER, Void.TYPE, Integer.TYPE, Integer.TYPE),

  4. InputSelectEntitiesFromList.java — publish min/max when a selection starts
  getController().getGui().setSelectables(vCards);
  getController().getGui().setSelectionRange(min, max);   // new

  5. FloatingZone.java — new Ctrl+0 branch in dispatchHotkey
  if (digit == 0) {
      final int need = fz.getMatchUI().getSelectionMin();
      if (need < 1) return false;
      int picked = 0;
      for (final CardPanel panel : fz.getCardPanels()) {
          if (picked >= need) break;
          if (!fz.getMatchUI().isSelectable(panel.getCard())) continue;
          fz.getMatchUI().getGameController().selectCard(panel.getCard(), null,
                  new MouseTriggerEvent(MouseEvent.BUTTON1, 0, 0));
          picked++;
      }
      return true;
  }
  (Logic folded into the existing digit block — we'd just relax the digit < 1 lower bound.)

@tool4ever
Copy link
Copy Markdown
Contributor

Let me know if you want the FPref.UI_SELECT_FROM_CARD_DISPLAYS code cleanup suggested above as part of this PR.

hmn I'm undecided if users might prefer having the preference actually GUI-exposed now that this PR increases its usage 🤔
let's wait a bit and see if we'll get some reactions after merging on Discord?

Comment on lines +1165 to +1169
// Reveal/Look modes (no RevealNumber) publicly disclose the full hand — keep non-selectable revealed cards visible
final String discardMode = sa.getParam("Mode");
final boolean fullHandRevealedToChooser = discardMode != null
&& !sa.hasParam("RevealNumber")
&& (discardMode.startsWith("Reveal") || discardMode.startsWith("Look"));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the reveal-full-hand flag be a parameter computed at the call site? A while ago I was trying to cut back on the number of places where the PlayerController has to dissect a SpellAbility in order to decide how to behave. Looks like only DiscardEffect would need it - BalanceEffect and ConniveEffect both only use the player's own hand.

@Jetz72
Copy link
Copy Markdown
Contributor

Jetz72 commented May 12, 2026

Screenshot 2026-05-12 213043

If the revealed zone is prompting the user to choose something (e.g. some number of cards to discard), maybe the prompt might replace the usual title text? Or does it already seem visible enough in the usual prompt space?

tool4EvEr and others added 3 commits May 12, 2026 17:41
When the local player is picking cards from a revealed opponent zone
(e.g. Mind Warp, Coercion), the FloatingZone now renders the current
prompt at the top of the window so the player doesn't have to glance
back to the main prompt panel while deciding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PlayerController.chooseCardsToDiscardFrom now takes the collection of
cards the chooser is allowed to see, rather than letting the controller
inspect the SpellAbility to decide visibility — addressing reviewer
feedback to stop dissecting SpellAbilities inside the controller.
DiscardEffect (the only caller that reveals extra cards) computes the
visible set at the call site; a 5-arg convenience overload keeps
BalanceEffect/ConniveEffect untouched.

Incidentally fixes a case where, with RevealNumber set and a DiscardValid
filter, revealed-but-invalid cards would vanish from the chooser's UI
even though the reveal had publicly disclosed them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@MostCromulent
Copy link
Copy Markdown
Contributor Author

If the revealed zone is prompting the user to choose something (e.g. some number of cards to discard), maybe the prompt might replace the usual title text? Or does it already seem visible enough in the usual prompt space?

Yeah I think this is worthwhile, otherwise its not immediately obvious what you're expected to do in window unless you cross reference with the prompt.

Screenshot 2026-05-13 073019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants