r/programming • u/nickproud • 5d ago
Coding Magic the Gathering in C# - PART TWO - The Impossible Magic Card
https://youtu.be/Xbfzye07Xrg31
u/AustinYQM 5d ago
You are going to want to make mana cost nullable since there is a difference between something costing {0} and something not having a mana cost at all.
23
u/DeeBoFour20 5d ago
I thought from the title the card would be Blood Moon. I worked on the XMage project a couple years back. If Blood Moon is on the battlefield, shock lands are supposed to always enter untapped but there was a long standing bug where they still would prompt the user to pay 2 life.
"As this enters the battlefield" effects are supposed to happen simultaneously with the card entering and can modify the card's properties (in the case of Multiversal Passage, the "as it enters" determines its type in addition to whether it's tapped or not). In XMage, we handled that part first before considering effects of other cards on the battlefield and 99% of the time it works but Blood Moon is an edge case.
There were ideas thrown around where the engine would modify properties of cards in hand but that caused other issues. You can still channel a Boseiju, Who Endures from hand for example.
Blood Moon is also a "fun" introduction to the magical rules of layering. Look into the interactions between Blood Moon and lands like Urza's Saga and Dark Depths which also change from time to time due to rules updates.
Also the mutate mechanic from Ikoria was a nightmare to implement (not sure if that ever got done either, those cards don't see much play these days).
Anyway, hope this doesn't discourage you because I'm interested in where you go with this. You can put those edge cases in the back of your mind for now and focus on getting a basic engine up if you like.
4
8
u/tantalor 4d ago
Bad use of OOP. This is just data. There is no behavior here.
These aren't objects. They are dumb data structures. They don't do anything.
You would be better off using an immutable data type, like record.
7
u/xhable 5d ago
Coming to punish the class hierarchy, just as threatened in the first video! 😄
I really like that you've deliberately let the original model fail before introducing composition. It's a much better demonstration of why you'd choose that approach rather than simply presenting the finished abstraction.
My main thought here is "LandAbility"... Producing mana isn't really a property of being a land. Llanowar Elves and Sol Ring produce mana too, and some lands don't produce any. Even Dryad Arbor gets its green mana ability from having the Forest subtype rather than from an explicitly defined ability.
21
u/derekmckinnon 5d ago
I never thought I’d see the intersection of my two dominant hobbies posted here 😳
In response, I will cast Upvote (sorry I can’t help myself…)
6
u/iamdimas 5d ago
I started this in C# and Unity a long time ago as well then forgot about it. Decided to start building an engine for MTG recently (in Rust even though I don't know it) with the help of AI and I learned that interactions get real complex when you get deep into the rules and interactions. Currently I'm just trying to work my way out of all the technical debt I built up lol
5
u/Manbeardo 5d ago
The legality of attacks/blocks is hilarious because the attack/block is only illegal if you can find a set of attacks/blocks that violates fewer restrictions than the one being made, so you technically have to check all possible combinations before you can say the attack/block is illegal.
2
2
u/Dalput 4d ago
Good video on composition vs inheritance. I would personally avoid having specific members like LandAbility and CreatureStats though. Instead, each card would simply have stats and a collection of abilities. Where lands cards would simply be cards that do not make use of the stats. Land ability would just be a mana production ability, which any/all type could possibly have: Lands, creatures, artifacts, enchantments can all produce mana; even instances and sorceries could as well.
So in general, all cards should have a cost (land would hide their cost), types that would set certain data visible (creature type would show their power and toughness) and abilities. Where abilities would be a collection of a Ability which would have a cost (value, tap or other such additional conditions; possibly none) to do whatever they're meant to do (self/target/multi-targets gets/takes whatever).
Name - string
Cost - int
Types - collection of enum-types (possibly a hashset)
Abilities - collection of ability
Buff/debuff would be non-activation abilities on the object that would occur when necessary (observer pattern with phases or the modification of stats etc)
1
u/TheRealPomax 4d ago
No, it doesn't. And this clickbait title thing has really outstayed its welcome.
1
1
u/nicolas-siplis 2d ago
Hey! Not trying to steal your thunder here, but I figured maybe you'd be interested in something I've been building. It's a full MTG engine supporting all paper-legal cards, and the way it works is by acting as a 'card compiler'. It takes oracle text and turns that into an AST which then becomes compiled in-game effects. Then the reverse process, a decompilation of sorts, gets you readable but dynamic oracle text in the card text, which means text-altering effects like from Artificial Evolution pretty much 'just work'.
https://github.com/chiplis/ironsmith
I'm not sure how far you intend to take this but I figured maybe you'd wanna look at a farther along project for some ideas/unblocking. As a general word of advice, I think you'll want to avoid inheritance as much as possible here and focus fully on composition. Game objects are just way too dynamic and non-hierarchical for OOP to be a good enough fit, or at least that's been my impression during development.
34
u/TurboJetMegaChrist 5d ago
The classic "is a" vs "has a". Subclasses are often more painful than freely mix-and-matching a bunch of properties via interface or decorator.
This only gets more important when you want to model static abilities like [[Blood Moon]] or [[Mirror Gallery]]