-
Notifications
You must be signed in to change notification settings - Fork 831
Expanded yield system / auto-pass options #9643
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Adds feature-gated yield options to reduce clicking in multiplayer games: - 3 yield modes: Until Stack Clears, Until End of Turn, Until Your Next Turn - Right-click End Turn button for yield menu - Keyboard shortcuts (Ctrl+Shift+S, Ctrl+Shift+N) - Smart yield suggestions when player can't respond - Configurable interrupt conditions via Game menu - Master toggle in preferences (default OFF) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add checkbox in Gameplay section (after Auto-Yield) - Hide yield keyboard shortcuts when feature disabled - Update description to cover all features Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix yield not auto-passing: mayAutoPass() now delegates to shouldAutoYieldForPlayer() for experimental yield modes - Fix keybind/menu not passing priority: added selectButtonOk() call after setting yield mode - Fix re-prompting when already yielding: added getYieldMode() method and isAlreadyYielding() check - Integrate suggestions into prompt UI instead of modal dialogs: suggestions now appear in prompt area with Accept/Decline buttons - Fix "no actions" prompt not firing: hasAvailableActions() now checks actual playability via getAllPossibleAbilities() instead of just checking if hand is non-empty Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix interrupt conditions to only trigger when player is specifically attacked (using getAttackersOf instead of getDefenders.contains) - Separate UNTIL_END_OF_TURN and UNTIL_YOUR_NEXT_TURN end conditions: - UNTIL_END_OF_TURN now clears on UNTAP phase of any new turn - UNTIL_YOUR_NEXT_TURN clears when player's turn starts - Remove yieldTurnOwner/yieldTurnNumber tracking (simplified approach) - Fix menu checkboxes to stay open when toggled Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Documents the yield system rework including: - Feature overview and yield modes - Smart yield suggestions - Interrupt conditions (with multiplayer scoping) - Technical implementation details - Testing guide and changelog Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- End Turn button now uses experimental yield system when enabled - Exclude triggered abilities from "opponent spell" interrupt (targeted triggers handled by "targeting" interrupt instead) - Move Auto-Yields into Yield Options submenu when experimental enabled - Track turn number for UNTIL_END_OF_TURN to respect phase stops - Fix yield re-enable after interrupt (track turn on first check) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Clarifies that all code was written by Claude AI under human instruction. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New features: - UNTIL_BEFORE_COMBAT: Yield until entering combat phase (F3) - UNTIL_END_STEP: Yield until end step phase (F4) - Updated hotkeys: F1-F5 for yield modes, ESC to cancel yield Bug fixes: - UNTIL_STACK_CLEARS now checks simultaneous stack entries - UNTIL_END_OF_TURN no longer interrupted by combat on own turn Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add turn/phase tracking for UNTIL_END_STEP yield mode (same pattern as combat) - Add tracking for UNTIL_YOUR_NEXT_TURN to handle clicks during own turn - Rename "End Turn" button to "Next Turn" for clarity - Update tooltips to accurately describe yield behavior - Update documentation to reflect new tracking logic Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Auto-suppress declined suggestions until next turn with hint text - Fix PlayerView instance matching in yield methods (map key bug) - Add yield button priority over smart suggestions - Extend reveal interrupt to cover opponent choices - Disable yield buttons during cleanup/discard phase - Add isBeingAttacked helper for planeswalker/battle attacks - Change YIELD_INTERRUPT_ON_REVEAL default to false - Update documentation with all changes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a new interrupt condition that triggers when an opponent casts a mass removal spell (board wipes, exile all, etc.) that could affect the player's permanents. Detects DestroyAll, ChangeZoneAll (with exile/graveyard destination), DamageAll, and SacrificeAll effects. Only interrupts if the player has permanents matching the spell's ValidCards filter - empty board means no interrupt. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New Features:
- UNTIL_NEXT_PHASE yield mode that clears on any phase transition
- Dynamic hotkey display in tooltips and prompts based on user preferences
- Button layout reordered: Next Phase, Combat, End Step / End Turn, Your Turn, Clear Stack
Hotkey defaults (F1-F6):
- F1: Next Phase (new)
- F2: Combat
- F3: End Step
- F4: End Turn
- F5: Your Turn
- F6: Clear Stack
Files changed:
- YieldMode.java: Added UNTIL_NEXT_PHASE enum
- YieldController.java: Phase tracking, dynamic cancel key display
- VYield.java: New button, dynamic tooltip updates
- CYield.java: Action listener and highlight logic
- KeyboardShortcuts.java: New shortcut action
- ForgePreferences.java: New preference, reordered F-keys
- en-US.properties: Localization with {0} placeholders for hotkeys
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Applied 10 amendments to improve documentation quality:
CRITICAL FIXES:
1. Add missing YieldController.java to file lists and architecture
- YieldController is a core component that was completely missing
- Updated "New Files" section from 3 to 4 files
2. Add comprehensive Architecture section
- Component hierarchy diagram showing GUI layer organization
- Detailed explanation of YieldController, AbstractGuiGame interaction
- Network independence architecture with multi-player scenarios
- Complete data flow diagrams for all yield operations
- File organization with clear component responsibilities
MODERATE IMPROVEMENTS:
3. Expand State Management section with delegation pattern
- Show AbstractGuiGame's lazy initialization of YieldController
- Document YieldCallback interface implementation
- Add complete state map documentation from YieldController
- Include mode-specific end condition table
4. Correct Modified Files count and descriptions
- Updated from 14 to 13 files (removed EDocID, FButton, ProtocolMethod)
- Improved descriptions to match actual implementation
- Clarified that network protocol has no yield-specific changes
MINOR ENHANCEMENTS:
5. Clarify Yield Options Panel button layout
- Document 2-row layout structure
- Add visual feedback section
- Note cleanup/discard phase button disabling
6. Add PlayerView lookup technical detail
- Document TrackableTypes.PlayerViewType.lookup() usage
- Explain Map key consistency requirement
7. Expand Smart Yield Suggestions section
- Add preference names for each suggestion type
- Document auto-suppression behavior in detail
- Clarify when suggestions appear vs. don't appear
- Explain yield button priority over suggestions
8. Add Initial Implementation changelog entry
- Document YieldController architecture rationale
- Explain delegate and callback pattern choices
- Note lazy initialization strategy
9. Fix typo: Make "disabled" bold for clarity in interrupt section
10. Add comprehensive Troubleshooting section
- Yield activation issues
- Unexpected yield clearing
- Smart suggestion behavior
- Network play expectations
- Performance considerations
The documentation now accurately reflects the codebase implementation,
includes the requested Architecture section explaining component
interactions, and provides helpful troubleshooting guidance for users.
https://claude.ai/code/session_01SBGxSAqnqbpVuEsinNtrnZ
…-OorXS Comprehensively update DOCUMENTATION.md for accuracy and completeness
DOCUMENTATION.md now contains all PR documentation, making the separate .documentation/YieldRework-PR.md file redundant. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file shouldn't be added to the main repo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's good for following the AI reasoning process at this stage
but yea when it gets finalized for merge some of the content should land in docs/ for end users (Wiki)
and non trivial code aspects would be fine as Javadoc only
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have committed draft wiki documentation for review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems appropriate to me
forge-gui/src/main/java/forge/gamemodes/match/input/InputPassPriority.java
Outdated
Show resolved
Hide resolved
The yield interrupt for "targeted by spell or ability" was not triggering for Oona, Queen of the Fae's ability because the targeting is in a sub-ability (DB$ Dig), not the main ability (AB$ ChooseColor). Modified targetsPlayerOrPermanents() to recursively check sub-instances via getSubInstance(), ensuring targeting in nested sub-abilities is properly detected. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Per PR review feedback from tool4ever: hasAvailableActions was identical to canRespondToStack. Removed the duplicate and reuse canRespondToStack in shouldShowNoActionsPrompt. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Refactored YieldController to use network-safe GameView properties instead of gameView.getGame() which returns an unsynchronized dummy object for network clients. - Use gameView.getPhase/getTurn/getPlayerTurn/getStack/getCombat - Use CombatView and StackItemView instead of direct Game access - Added StackItemView.getApiType() for mass removal detection Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Testing by PotionSTAT has revealed an issue with network play integration not realised before PR. YieldController was using gameView.getGame() which returns an unsynchronized dummy object for network clients. Have refactored to use network-safe GameView properties and added StackItemView.getApiType() for mass removal detection. Will also check implications for smart prompts. |
|
Re: Network Code Changes While the yield system was designed to avoid network protocol changes, in testing discovered that smart suggestions in InputPassPriority had a network compatibility issue requiring new TrackableProperties. The solution: Add two TrackableProperties (HasAvailableActions, WillLoseManaAtEndOfPhase) to PlayerView. These piggyback on existing GameView serialization—no protocol changes required. The host calculates these values and they're automatically included when GameView is sent to clients. The yield system remains client-local; this ensures the data for yield suggestions is synchronized, while yield decisions stay independent per client. The alternative to making these network changes would have been implementing a more basic heuristic for the yield prompts which would have been a worse and experience for the player. |
- Add HasAvailableActions and WillLoseManaAtEndOfPhase TrackableProperties - Refactor InputPassPriority to use PlayerView/GameView instead of transient Game - Fix suggestions appearing immediately after yield ends (yieldJustEnded tracking) - Fix wrong yield mode when clicking yield buttons (legacy set interference) - Add PlayerView lookup to autoPassUntilEndOfTurn/autoPassCancel methods Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add setYieldModeSilent() to break infinite recursion when syncing yield state between server and client (stack overflow fix) - Fix getPlayerView() in InputPassPriority to use network-synchronized PlayerView from GameView instead of local Player object - Enhance hasAvailableActions() to actually check mana availability using heuristic (CostPartMana.canPay() always returns true) - Smart suggestions now correctly trigger when player has cards but can't afford any spells Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ayout - Add populate() calls to SLayoutIO.openLayout() and revertLayout() after loading completes, following the pattern used in FControl.setCurrentScreen() - This fixes yield panel not reappearing after View > Refresh Layout or Layout > Open operations - Incidentally fixes the same issue for other dynamic panels (dev mode, etc.) - Add stale parent cell detection in VMatchUI to handle layout refresh - In 2-player games, move Clear Stack button to middle position since Your Turn button is not shown (only relevant for 3+ player games) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create user-facing documentation for the experimental yield system: - Yield modes and their end conditions - Access methods (panel, right-click menu, keyboard shortcuts) - Smart yield suggestions - Configurable interrupt conditions - Troubleshooting guide Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| * always returns true. We estimate available mana from floating mana plus | ||
| * untapped mana sources and compare to spell CMCs. | ||
| */ | ||
| public boolean hasAvailableActions() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was actually considering a way where the feature doesn't need its own checkbox but the changes in this class alone make that impossible:
this costs way too much performance and certainly should only be performed when it's actually needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a preferred way of dealing with this? I raised with the AI coder and its suggested response was:
Option 1: Change to only compute hasAvailableActions() if the smart prompts are enabled by the user; or
Option 2: Adopt a less accurate heuristic with lower performance cost:
Remove:
1. hasAvailableActions() method from Player.java
2. updateAvailableActionsForView() method from Player.java
3. The TrackableProperty from PlayerView.java
4. The call in PhaseHandler.passPriority()
Replace with: Simpler heuristics using existing View data
(like shouldShowNoManaPrompt already does):
private boolean shouldShowStackYieldPrompt() {
// ... existing null/stack checks ...
// Simple heuristic: no mana = can't respond to stack
return !hasManaAvailable(pv);
}
private boolean shouldShowNoActionsPrompt() {
// ... existing null/turn checks ...
// Simple heuristic: empty hand + no mana = nothing to do
FCollectionView<CardView> hand = pv.getHand();
boolean emptyHand = (hand == null || hand.isEmpty());
return emptyHand || !hasManaAvailable(pv);
}
Trade-offs:
- Pro: Zero performance cost - uses already-synced View
properties
- Pro: No new TrackableProperties needed
- Con: Less accurate - misses free spells, 0-cost abilities,
etc.
- Con: Won't detect "has cards but can't afford any of them"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you say "the feature doesn't have its own checkbox" do you mean expanded yields are just integrated by default rather than being an experimental option in the Preferences menu?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the code should probably also be moved to the View classes to keep the engine clean
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you say "the feature doesn't have its own checkbox" do you mean expanded yields are just integrated by default rather than being an experimental option in the Preferences menu?
yea that's what I meant but I feel like this is turning more into a power user feature anyway and it might be better for the new user experience having the GUI less crowded at first
this also relates to how it intersects with the existing system, but I'll make a separate comment on that later
based on that Option 1 probably makes more sense, if the heuristics are broadened it might also degrade usefulness too much imo
thanks
|
|
||
| private PlayerView getPlayerView() { | ||
| return getController().getPlayer().getView(); | ||
| // For network clients, we need to get the PlayerView from the GameView |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still need to investigate this, seems rather hacky
- but if it's needed it should reuse
lookupPlayerViewById
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll address the duplicate function issue now, and can come back to this if you find a better approach.
Move lookupPlayerViewById to IGuiGame interface and reuse in InputPassPriority instead of duplicating the ID-based lookup logic. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| SHORTCUT_MACRO_RECORD ("16 82"), | ||
| SHORTCUT_MACRO_NEXT_ACTION ("16 50"), | ||
| SHORTCUT_CARD_ZOOM("90"), | ||
| SHORTCUT_YIELD_UNTIL_NEXT_PHASE("112"), // F1 key |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't mind a few more shortcuts but right now F1 is already hardcoded to Help so this doesn't even work
while some more flexibility in the system might be nice, considering this PR size already it shouldn't be done here
| // Smart yield suggestion helper methods | ||
|
|
||
| private boolean isExperimentalYieldEnabled() { | ||
| return FModel.getPreferences().getPrefBoolean(FPref.YIELD_EXPERIMENTAL_OPTIONS); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given that this feature access is desktop only right now you should also check for mobile GUI
otherwise I'd imagine this will still get enabled and cause some confusion (e.g. for users who switch between them to play adventure)
if you want to support both better leave it for a follow-up, I don't think I'd survive the code growth from that alone... 😅
Summary
Inspired by recent discussion on the Forge Discord, this PR adds an expanded yield system to reduce click fatigue in multiplayer games and speed up play. Changes are extensively documented in Documentation. Fully backwards compatible with current functionality: the feature is disabled by default and must be explicitly enabled in preferences.
Problem
In multiplayer Magic games (3+ players), the current priority system requires excessive clicking:
Solution
Expanded yield options that give players more flexibility to control yield timing and conditions. The aim is to help players spend less time responding to unnecessary priority prompts and let them get to their desired phase of action quicker:
Particularly helpful for that one friend who won't pay attention to priority prompts in Commander.
Key Design Decisions
Testing
Changes have been tested in local play (thanks to PotionSTAT for testing and feedback). As changes are purely on the GUI layer and do not involve network logic, there is no reason to think testing would not replicate in network use cases.
Code authored by Claude Code (Opus 4.5) under human direction and review.