Skip to content

DanDan Variant#10232

Draft
agylesox wants to merge 48 commits intoCard-Forge:masterfrom
agylesox:master
Draft

DanDan Variant#10232
agylesox wants to merge 48 commits intoCard-Forge:masterfrom
agylesox:master

Conversation

@agylesox
Copy link
Copy Markdown
Contributor

@agylesox agylesox commented Apr 1, 2026

Summary

Implements Dandan as a game mode creating shared zones and match rules in the engine, plus Dandan-specific layout and zone/hand presentation in the desktop client.


Gameplay / rules engine

  • Match setup: Dandan uses a shared deck and shared Library / Graveyard wiring; sideboarding is disabled for this mode where applicable (forge-game, e.g. Match, GameRules).
  • Drawing and zones: Draws resolve against the canonical shared library; zone routing uses the shared player’s zones so all players see one pile (forge-game, e.g. Player, GameAction).
  • Rules that touch shared graveyard: Targeting / validity / restrictions account for a shared graveyard so cards there behave correctly for Dandan (forge-game, e.g. CardProperty, SpellAbilityRestriction related to card ownership and card controller rules).
  • Ownership and control for cards in graveyard are handled by calling GameRules.relaxesControllerOwnershipForCardProperties(card.getZone()). This is extensible to other zones, but for DanDan this only impacts the graveyard.
  • Canonical “view” of shared zones (engine-side): DanDanViewZones exposes a single consistent card sequence for library/graveyard for UIs and tooling that consume GameView (forge-game).

UI / client

  • Match layout: Dandan matches use the Dandan layout file instead of the default match layout when rules say Dandan, so the shared graveyard is visible in the middle of the screen (CMatchUI, ForgeConstants / MATCH_DANDAN_LAYOUT_FILE).

Out of scope

  • Changes to AI to include DanDan specific heuristics for evaluating top of the library.

agylesox added 30 commits March 30, 2026 17:43
@Jetz72
Copy link
Copy Markdown
Contributor

Jetz72 commented Apr 1, 2026

Ambitious project. The concept of shared zones seems useful to a few other variant formats (e.g. Shared Planar deck option, Bounty deck), though it looks like your implementation results in a lot of DanDan-specific checks and branches dotted throughout the code. I wonder if the concept can be generalized.

Have you tested the shared zones with more obscure mechanics, such as subgames and Karn's game restart effect?

I feel some things like your deck comment support and game simulation changes could stand alone as separate PRs.

@agylesox
Copy link
Copy Markdown
Contributor Author

agylesox commented Apr 1, 2026

  • I think the Dandan specific checks mostly sit in the CardProperty due to needing to ignore ownership and control when dealing with the shared graveyard. But you are right and I agree, that is clunky. I will see if there's a better way to handle that.
  • I did not check how it works with Karn or Shaharazad. I'll check and see what happens. I think one of the discord members has a Shaharazad version that would probably be a good test.
  • Deck comment support and Game sim changes - you're right. Those could have been / should have been separate PRs. They got lumped into this because they were tools I needed as I was going along, but I can separate them if that's helpful.

@Jetz72
Copy link
Copy Markdown
Contributor

Jetz72 commented Apr 1, 2026

  • They got lumped into this because they were tools I needed as I was going along, but I can separate them if that's helpful.

Helps a bit by slimming down what needs reviewing here, but also it'd probably get those parts merged faster. I suspect reviewing this will have to wait a bit for most of the team, now that we're in the middle of reveal season.

I think the Dandan specific checks mostly sit in the CardProperty due to needing to ignore ownership and control when dealing with the shared graveyard. But you are right and I agree, that is clunky. I will see if there's a better way to handle that.

Might extract things like getController.equals( into some helper methods, to check if a card or even a zone is owned/controlled by a player. Can pack the weird edge case of shared zones into that and then do some structural searches of the code base to figure out all the places where the helpers can be inserted.

@agylesox
Copy link
Copy Markdown
Contributor Author

agylesox commented Apr 1, 2026

Have you tested the shared zones with more obscure mechanics, such as subgames and Karn's game restart effect?

Shahrazad does appear to be working in DanDan mode.

  • Spawns the sub-game from the parent with the remaining library
  • If a sub-game spawns from the sub-game, it spawns with the remaining sub-game library
  • Losing a sub-game puts you back to the game that spawned it, with the loser losing half their life. Cards from the subgame are shuffled and become the library of the spawning game and the spawning game resumes.

Playtesting through the UI, I think I got to 4 sub-games deep, including resolving a sub-game, returning to the spawning game and then launching another sub-game.

Update: Karn Liberated also works.

@agylesox
Copy link
Copy Markdown
Contributor Author

agylesox commented Apr 3, 2026

I feel some things like your deck comment support and game simulation changes could stand alone as separate PRs.

Moved comment on mouse hover to PR #10263 and removed the extra simulation mode changes from this commit. The only simulation mode changes are not related to having the new game type of DanDan

@agylesox agylesox marked this pull request as draft April 3, 2026 20:06
@agylesox
Copy link
Copy Markdown
Contributor Author

agylesox commented Apr 6, 2026

Might extract things like getController.equals( into some helper methods, to check if a card or even a zone is owned/controlled by a player. Can pack the weird edge case of shared zones into that and then do some structural searches of the code base to figure out all the places where the helpers can be inserted.

In CardProperty and SpellAbilityRestricion, the if statement check includes a call to GameRules.relaxesControllerOwnershipForCardProperties(cardZone). While this gets the ownership/control check in one place, it still leaves the giant IF statement in CardProperty.

One thought is refactoring the IF statement to separate out the control/owernship properties with an enumset, so for that set, the check can just happen once. But that would break up the simplicity of having all the checks in one long IF. If other properties need to be grouped together that might make sense?

@agylesox agylesox marked this pull request as ready for review April 6, 2026 12:07
@Jetz72
Copy link
Copy Markdown
Contributor

Jetz72 commented Apr 6, 2026

In CardProperty and SpellAbilityRestricion, the if statement check includes a call to GameRules.relaxesControllerOwnershipForCardProperties(cardZone). While this gets the ownership/control check in one place, it still leaves the giant IF statement in CardProperty.

You could keep the property.startsWith blocks while still shifting the contained logic into methods on the card itself.

Eventually it'd be nice to break down that giant if-tree into something more performant, but that'll be a bigger refactoring someday.

@agylesox agylesox marked this pull request as draft April 7, 2026 12:01
@agylesox agylesox marked this pull request as draft April 7, 2026 12:01
@agylesox
Copy link
Copy Markdown
Contributor Author

agylesox commented Apr 7, 2026

Need to put back into PR draft and fix

  • graveyard to hand needs to ignore ownership
    • currently cards returning to hand from graveyard return to the hand of the original player, not the caster of the spell)
    • e.g. regrowth will put a spell in your opponents hand if they cast it originally
  • library to battlefield needs to ignore ownership
    • currently fetch lands will put a card on the battlefield of the "owner", which may or may not be the person using the fetch.

@Jetz72
Copy link
Copy Markdown
Contributor

Jetz72 commented Apr 7, 2026

That touches on one of my concerns with this addition. I worry that this niche format ends up breaking too many assumptions that the engine and game rules make about how things work. Should an effect that says "Return a creature from an opponent's graveyard to its owner's hand" still mean the other player's hand because it described their graveyard, or should it go to your hand instead because it didn't specifically say anyone else's hand? The rules for DanDan say the person who plays a card from hand becomes the owner, but who owns cards that are milled, tutored to the battlefield, or technically even just Suspended? I think before you can implement the format in a rules engine, you kinda need to pin down the full breadth of the modifications it makes to Magic's comprehensive rules, and scrutinize those changes to make sure they work intuitively with the rules text of cards.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants