Skip to content

Add playable card highlights#10680

Open
autumnmyst wants to merge 8 commits into
Card-Forge:masterfrom
autumnmyst:actionable-highlights
Open

Add playable card highlights#10680
autumnmyst wants to merge 8 commits into
Card-Forge:masterfrom
autumnmyst:actionable-highlights

Conversation

@autumnmyst
Copy link
Copy Markdown
Contributor

@autumnmyst autumnmyst commented May 15, 2026

Adds a configurable outline on cards the local player can currently play or activate. Off by default; toggled under "Highlight Actionable Cards" in desktop preferences and mobile settings.

Sources the highlight set from the same AvailableActions heuristic APINA reads, so highlights and the auto-pass decision share one scan per priority window.

During cost payment the AI heuristic isn't meaningful, the player isalready paying, so highlights switch to marking any card with a playable mana ability, scanned across Hand/Battlefield/Graveyard/Exile/ Command. For declare-attackers and declare-blockers the heuristic isn't used either; combat legality is deterministic, so candidates come from CombatUtil.canAttack (undeclared, legal attackers) and CombatUtil.canBlock (defenders that can block at least one current attacker). Both combat sets refresh after each click so highlights track the in-progress declaration.

Architecture

  • AvailableActions (forge-ai) gains collectActionable(player, timeoutMs), a full-scan sibling of compute(...). Same canPayManaCost + isFullyTargetable predicates over Hand / Battlefield / Flashback. Timeout fallback over-highlights.
  • PlayerControllerHuman.chooseSpellAbilityToPlay runs the scan once per priority window when APINA or highlights need it, caches the result, and derives APINA's hasAvailableActions from !actionable.isEmpty() against the same set.
  • Per-input push API on PCH: pushActionableCards, pushAttackerCandidates, pushBlockerCandidates. Lifecycle: push on showMessage, clear on onStop, re-push after each click in combat and cost-payment inputs.
  • GUI plumbing: IGuiGame.setWeaklySelectable / clearWeaklySelectable, stored on AbstractGuiGame. RemoteClientGuiGame ships the set over two new ProtocolMethod entries. CardPanel (desktop) and CardRenderer (mobile) draw a thin outline when the pref is on.
  • Prefs: UI_SHOW_ACTIONABLE_HIGHLIGHTS (default off) and UI_ACTIONABLE_HIGHLIGHT_COLOR (default 66CCFF). Color input is a case-insensitive 6-char RGB hex, normalized to uppercase on save.

Comment thread forge-ai/src/main/java/forge/ai/AvailableActions.java Outdated
Comment thread forge-ai/src/main/java/forge/ai/AvailableActions.java Outdated
Comment thread forge-ai/src/main/java/forge/ai/AvailableActions.java Outdated
@MostCromulent MostCromulent added Enhancement New feature or request GUI labels May 18, 2026
@autumnmyst autumnmyst force-pushed the actionable-highlights branch from 9262678 to 2222cf5 Compare May 18, 2026 19:42
@mermerico
Copy link
Copy Markdown

I'm very excited for this, it's a big quality of life win! In my local prototype implementation I had a highlight specifically for the cards auto-pay is going to use. That made it easier to know when I could trust auto-pay and made me use it more often. Is that something that you would be interested in putting in this PR or should it be proposed as a separate PR?

@tool4ever
Copy link
Copy Markdown
Contributor

@mermerico
separate PR please, also see #5112 for the bigger picture

Comment thread forge-gui-mobile/src/forge/card/CardRenderer.java Outdated
Comment thread forge-ai/src/main/java/forge/ai/AvailableActions.java Outdated
@autumnmyst autumnmyst force-pushed the actionable-highlights branch from 0485441 to b43c5bd Compare May 19, 2026 19:12
@MostCromulent
Copy link
Copy Markdown
Contributor

MostCromulent commented May 19, 2026

Does this work correctly for network play?

Remote clients will need a way to tell server they have highlights enabled so server runs the longer AvailableActions scan, otherwise it will just use whatever the server hosts preference is.

I think preference probably needs to be threaded through same start of match client -> server prefs seed packet as yield settings in YieldController, and PlayerControllerHuman needs to consult the yield controller-backed per-player preference when doing the scan rather than host pref.

I can deal with that in quick follow-up PR if we would rather just get this landed first.

@tool4ever
Copy link
Copy Markdown
Contributor

Could also look into using JColorChooser in a follow-up

Comment thread forge-gui/src/main/java/forge/player/PlayerControllerHuman.java
Comment thread forge-gui-desktop/src/main/java/forge/view/arcane/CardPanel.java
@autumnmyst
Copy link
Copy Markdown
Contributor Author

autumnmyst commented May 19, 2026

Does this work correctly for network play?

Remote clients will need a way to tell server they have highlights enabled so server runs the longer AvailableActions scan, otherwise it will just use whatever the server hosts preference is.

I think preference probably needs to be threaded through same start of match client -> server prefs seed packet as yield settings in YieldController, and PlayerControllerHuman needs to consult the yield controller-backed per-player preference when doing the scan rather than host pref.

I can deal with that in quick follow-up PR if we would rather just get this landed first.

That's true, good call. Currently host-off, client-on doesn't show anything to the client. All the other combinations should work though, since so long as the host has it on it will calculate highlights for each local player instance. If I can fix it with a small change I'll do it, but otherwise you're welcome to fix it in a follow up.

@autumnmyst autumnmyst force-pushed the actionable-highlights branch 2 times, most recently from 33a6a8e to ec81ee1 Compare May 20, 2026 17:14
@MostCromulent
Copy link
Copy Markdown
Contributor

MostCromulent commented May 20, 2026

Should add a checkbox for Highlight actionable cards to the in-match Display submenu, so you don't always need to go to preferences to enable/disable the setting.

A light blue outline ("weakly selectable") on cards the player can
currently play or activate. Reads the same AvailableActions heuristic
as APINA, so the highlight set and the auto-pass decision come from
one pass per priority window.

- IGuiGame: setWeaklySelectable / clearWeaklySelectable; default
  implementations on AbstractGuiGame; remote-proxy implementations on
  RemoteClientGuiGame ship over the wire as new ProtocolMethods.
- PlayerControllerHuman:
  - cachedActionableCards from chooseSpellAbilityToPlay reused by
    pushActionableCards when not in payment mode.
  - pushAttackerCandidates / pushBlockerCandidates compute candidate
    sets from CombatUtil for InputAttack / InputBlock.
  - Payment-mode pushActionableCards falls back to the simpler "card
    has a playable mana ability" predicate.
- InputAttack / InputBlock: push candidates on showMessage and after
  every click; clear on stop. Stops highlights persisting through
  autopass on remote clients.
- InputPassPriority / InputPayMana: push at message time, clear on
  stop, refresh on each cost component selection.
- CardPanel (desktop) / CardRenderer (mobile): honor isWeaklySelectable
  with a thin blue outline in the existing render paths.
- VSubmenuPreferences + CSubmenuPreferences: UI toggle for
  UI_SHOW_ACTIONABLE_HIGHLIGHTS in the prefs page.
- en-US: cbShowActionableHighlights / nlShowActionableHighlights.

Defaults to off; gated on UI_SHOW_ACTIONABLE_HIGHLIGHTS.
Adds a hex RGB input on both desktop and mobile preference pages that
controls the outline color used for the actionable-card highlight.
Defaults to 66CCFF (the existing light blue).

- ForgePreferences: new UI_ACTIONABLE_HIGHLIGHT_COLOR pref (default
  "66CCFF").
- Desktop VSubmenuPreferences/CSubmenuPreferences: text field under
  the highlight toggle. Accepts a case-insensitive 6-char RGB hex and
  stores the uppercase form; invalid input reverts the field to the
  persisted value rather than persisting garbage.
- Mobile SettingsPage: new HexColorSetting opens an input dialog beside
  the highlight toggle. Same normalization on save.
- CardPanel (desktop) / CardRenderer (mobile): parse the stored 6-char
  value at draw time; fall back to default on any parse failure.
  Validation happens once on the write side so reads don't repeat it.
…l, forecast, etc.), affected both highlights and APINA
The host runs the AvailableActions scan on a remote player's behalf, so the
decision to do the full collectActionable walk (vs the cheap early-exit) must
use that client's highlight setting, not the host's. Thread
UI_SHOW_ACTIONABLE_HIGHLIGHTS through the existing client->host pref seed
(SYNCED_PREFS) and read it via yieldController.getBoolPref at every scan-gating
point in PlayerControllerHuman instead of FModel. Renderer reads stay on FModel
since they run in the client process.
…ar, actionable or otherwise. Added highlight toggle in in-game forge>view menu
@autumnmyst autumnmyst force-pushed the actionable-highlights branch from ec81ee1 to d1cc96a Compare May 31, 2026 18:34
@autumnmyst
Copy link
Copy Markdown
Contributor Author

Should add a checkbox for Highlight actionable cards to the in-match Display submenu, so you don't always need to go to preferences to enable/disable the setting.

Done, added an in-game toggle under Display > Highlight Actionable Cards

One thing to note is it doesn't refresh upon toggling, the reason I didn't is 1. it's a very minor edge case and it will refresh on the next game action, a fix would be a lot of LoC added to the PR 2. I'm guessing whatever plumbing needs to be done for online games needs touch this path anyways, so it would be a lot of changes that would then need to be updated again in the fix for remote client highlights being independent of host highlights.

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

Labels

Enhancement New feature or request GUI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants