Sign in to follow this  
kevin_06s

Implementing Card Effects In A TCG

Recommended Posts

I am working on a trading card game (TCG) project, and I have run into a roadblock in deciding how to implement the special effects and abilities of the cards.
I am working in Java, by the way, although this is really a problem in object oriented design.

As it is now, I have a sort of event based setup where there is a main Card class that has methods like onAttack(), onDefend(), onPlay(), onDestroyed(), etc... which are called as events happen.
The stumbling block here is that I need to create a new subclass for every single card with an effect, which, while it would work just fine, somehow seems fundamentally wrong to me in the same way that it seems wrong to me when someone does array[0] = 0; array[1] = 0; array[2] = 0; etc., when they could just use a for loop.

This approach that I am using seems like it would scale very badly as the number of cards increases, and I am wondering if there is a way to simplify this design?
I am also wondering if anyone knows how some commercial TCGs get around this problem, or even if anyone has any source code from a TCG that I can study.

Share this post


Link to post
Share on other sites
[quote name='kevin_06s' timestamp='1305605952' post='4811768']
As it is now, I have a sort of event based setup where there is a main Card class that has methods like onAttack(), onDefend(), onPlay(), onDestroyed(), etc... which are called as events happen.
The stumbling block here is that I need to create a new subclass for every single card with an effect
[/quote]
This rudimentary design is far too unstructured. Everything your cards do is actually an aggregate of standard effects (effect types implicit in your method names are destroy a card, force something to attack or not attack (or to defend or not defend), forbid some cards to be played), which should be their own classes or functions; they, in turn, will probably rely on an even more stable layer of elementary game state alterations, evaluation of predicates, selection of cards and other objects.
Different cards should be defined in databases and configuration files, all instances of [b]one [/b]Card class.

I suggest you study the [url="http://www.wizards.com/Magic/TCG/Article.aspx?x=magic/rules"]comprehensive rules of Magic: the Gathering[/url]; beneath a heap of complications (with whole sections required by a single troublesome card), there is a rather neat and formal definition of game entities and events, of effect types, of state based actions that take place automatically, of what players can do, of how conflicts between cards are resolved. I hope your game will be simpler, but you should strive for that level of exactness.

Share this post


Link to post
Share on other sites
[quote name='LorenzoGatti' timestamp='1305619589' post='4811827']
[quote name='kevin_06s' timestamp='1305605952' post='4811768']
As it is now, I have a sort of event based setup where there is a main Card class that has methods like onAttack(), onDefend(), onPlay(), onDestroyed(), etc... which are called as events happen.
The stumbling block here is that I need to create a new subclass for every single card with an effect
[/quote]
This rudimentary design is far too unstructured. Everything your cards do is actually an aggregate of standard effects (effect types implicit in your method names are destroy a card, force something to attack or not attack (or to defend or not defend), forbid some cards to be played), which should be their own classes or functions; they, in turn, will probably rely on an even more stable layer of elementary game state alterations, evaluation of predicates, selection of cards and other objects.
Different cards should be defined in databases and configuration files, all instances of [b]one [/b]Card class.

I suggest you study the [url="http://www.wizards.com/Magic/TCG/Article.aspx?x=magic/rules"]comprehensive rules of Magic: the Gathering[/url]; beneath a heap of complications (with whole sections required by a single troublesome card), there is a rather neat and formal definition of game entities and events, of effect types, of state based actions that take place automatically, of what players can do, of how conflicts between cards are resolved. I hope your game will be simpler, but you should strive for that level of exactness.
[/quote]

Sorry, what I meant was not that there was a variety of standard effect types, but rather that, for example, when a card is attacking, its onAttack(Card target) method might be called, or when it is being attacked, onDefend(Card attacker) might be called. This allows me to do a huge variety of logic for card effects which cannot be matched by using standard state alternations. Is there any way to retain the flexibility I want while not having to resort to making a new subclass for each card?

Share this post


Link to post
Share on other sites
[quote name='kevin_06s' timestamp='1305626656' post='4811858']
[quote name='LorenzoGatti' timestamp='1305619589' post='4811827']

This rudimentary design is far too unstructured. Everything your cards do is actually an aggregate of standard effects (effect types implicit in your method names are destroy a card, force something to attack or not attack (or to defend or not defend), forbid some cards to be played), which should be their own classes or functions; they, in turn, will probably rely on an even more stable layer of elementary game state alterations, evaluation of predicates, selection of cards and other objects.
Different cards should be defined in databases and configuration files, all instances of [b]one [/b]Card class.
[/quote]

Sorry, what I meant was not that there was a variety of standard effect types, but rather that, for example, when a card is attacking, its onAttack(Card target) method might be called, or when it is being attacked, onDefend(Card attacker) might be called. This allows me to do a huge variety of logic for card effects which cannot be matched by using standard state alternations. Is there any way to retain the flexibility I want while not having to resort to making a new subclass for each card?
[/quote]

Template methods along the lines of onAttack(), onDefend() etc. are a good way to structure turn processing, but you should use composition rather than inheritance to describe different cards with a single class, and different assemblies of a moderate number of card effects, rather than as separate classes.
For example, assuming rules more or less like Magic: the Gathering, a card (creature?) could have a list of "things that might trigger when it attacks", and the one and only Card.onAttack() implementation (there might be decorators and test ones that we don't care about) would go through the list, test whether triggers are skipped, and execute those that aren't.
I am of course implying a Trigger class with methods to test whether it actually executes (e.g. "if it is an odd turn"), to tell the game which choices the player has to make (e.g. "choose target creature A controlled by you"), to execute if possible (e.g. "heal creature A").
Triggers would have no more inheritance than, maybe, variants with different contexts of known information, like CreatureAttackTrigger (with knowledge of the attacking creature) or TurnEndTrigger (with knowledge of the active player). You can get away with classes in this case because there is a small and fixed set of events that could fire a trigger.
Triggers would reference elementary test, user interface and game state change operations (in the example, querying the turn number, an odd() predicate, querying who controls a creature and which player is attacking; asking for a creature; healing a creature respectively).
The same organization would apply to continuous abilities, replacements, and anything else.

And yes, there is "a variety of standard effect types", even if they start out as the effect of a single card. Considering the modest amount of things they can do (damage, drawing cards, etc.), they are even more standard. Can you provide examples of what you think requires arbitrary code in card subclasses? How fancy are your cards?

Share this post


Link to post
Share on other sites
[quote name='LorenzoGatti' timestamp='1305649114' post='4811980']
[quote name='kevin_06s' timestamp='1305626656' post='4811858']
[quote name='LorenzoGatti' timestamp='1305619589' post='4811827']
This rudimentary design is far too unstructured. Everything your cards do is actually an aggregate of standard effects (effect types implicit in your method names are destroy a card, force something to attack or not attack (or to defend or not defend), forbid some cards to be played), which should be their own classes or functions; they, in turn, will probably rely on an even more stable layer of elementary game state alterations, evaluation of predicates, selection of cards and other objects.
Different cards should be defined in databases and configuration files, all instances of [b]one [/b]Card class.
[/quote]

Sorry, what I meant was not that there was a variety of standard effect types, but rather that, for example, when a card is attacking, its onAttack(Card target) method might be called, or when it is being attacked, onDefend(Card attacker) might be called. This allows me to do a huge variety of logic for card effects which cannot be matched by using standard state alternations. Is there any way to retain the flexibility I want while not having to resort to making a new subclass for each card?
[/quote]

Template methods along the lines of onAttack(), onDefend() etc. are a good way to structure turn processing, but you should use composition rather than inheritance to describe different cards with a single class, and different assemblies of a moderate number of card effects, rather than as separate classes.
For example, assuming rules more or less like Magic: the Gathering, a card (creature?) could have a list of "things that might trigger when it attacks", and the one and only Card.onAttack() implementation (there might be decorators and test ones that we don't care about) would go through the list, test whether triggers are skipped, and execute those that aren't.
I am of course implying a Trigger class with methods to test whether it actually executes (e.g. "if it is an odd turn"), to tell the game which choices the player has to make (e.g. "choose target creature A controlled by you"), to execute if possible (e.g. "heal creature A").
Triggers would have no more inheritance than, maybe, variants with different contexts of known information, like CreatureAttackTrigger (with knowledge of the attacking creature) or TurnEndTrigger (with knowledge of the active player). You can get away with classes in this case because there is a small and fixed set of events that could fire a trigger.
Triggers would reference elementary test, user interface and game state change operations (in the example, querying the turn number, an odd() predicate, querying who controls a creature and which player is attacking; asking for a creature; healing a creature respectively).
The same organization would apply to continuous abilities, replacements, and anything else.

And yes, there is "a variety of standard effect types", even if they start out as the effect of a single card. Considering the modest amount of things they can do (damage, drawing cards, etc.), they are even more standard. Can you provide examples of what you think requires arbitrary code in card subclasses? How fancy are your cards?
[/quote]

Please tell me if I am understanding you correctly. You are saying to have a Trigger class which has methods that check for all the different kinds of logical conditions, and then a collection with the different possible types of effects. Each card would have different values indicating which effects out of that collection it can apply, and a list of which triggers to check.

The design looks something like this:
Enum Effects
Class Trigger and subclasses
Enum TriggerTypes
Class Card has TriggerTypes and Effects

Is this correct?

Share this post


Link to post
Share on other sites
You should script your card definitions with, presumably, XML files or something similar containing definition of cards that reference triggers and similar entities, either embedded (anonymous) or by name, and named or embedded definitions of triggers etc. that reference a set of primitive tests, game state changes and UI interactions. Naming and factoring out triggers and other abilities is useful for consistency and to reduce repetitions.

A factory that is aware of these "blueprints" can assemble Trigger, Card, etc. instances whenever it's asked for a certain card by name; primitives can be implemented with an enum and a big switch statement, but a straightforward application of the Command design pattern (with classes such as AttackingCreaturesCount, TargetCreatureChoice and GameLoss), seems nicer on paper (you can use reflection to infer class names from primitive names, avoiding the big switch statement).

I think tests on conditional triggers etc. should be implemented as simple expression trees, with node types to represent relational, arithmetic and boolean operators and primitive queries of the game state.

Generally speaking, classes (and enums) should reflect only the rules of the game and be independent of the set of cards, which should come from separate and interchangeable data files. This ensures that
[list][*]you can replace or delete cards freely[*]you don't have hacks in some card that bypass the rules of the game and cause problems[*]you can test, and reason about, a closed set of simple building blocks rather than an unlimited number of cards containing arbitrary code[/list]
Do you need types of effect other than triggers? Can you tell more about the rules of your game?

Share this post


Link to post
Share on other sites
It might be a little more advanced then you are looking to go at the moment. But for a similar problem I solved It using a db table store my entities(cards in your case) with python scripts for the different effects.

Each of your triggers would be a column in the table in which you’d either have null or a python script for the effect that occurs at that trigger. If you take MTG as example your triggers would be something like this:

Start of Turn

Card Drawn

Card Played

Card Destroyed

Attack Declared

Damage Dealt

End of Turn

In this way you would cycle through every card in play during each of those triggers and perform the effect.

Share this post


Link to post
Share on other sites
[quote name='LorenzoGatti' timestamp='1305709897' post='4812381']<br />You should script your card definitions with, presumably, XML files or something similar containing definition of cards that reference triggers and similar entities, either embedded (anonymous) or by name, and named or embedded definitions of triggers etc. that reference a set of primitive tests, game state changes and UI interactions. Naming and factoring out triggers and other abilities is useful for consistency and to reduce repetitions.<br /><br />A factory that is aware of these &quot;blueprints&quot; can assemble Trigger, Card, etc. instances whenever it's asked for a certain card by name; primitives can be implemented with an enum and a big switch statement, but a straightforward application of the Command design pattern (with classes such as AttackingCreaturesCount, TargetCreatureChoice and GameLoss),  seems nicer on paper (you can use reflection to infer class names from primitive names, avoiding the big switch statement).<br /><br />I think tests on conditional triggers etc. should be implemented as simple expression trees, with node types to represent relational, arithmetic and boolean operators and primitive queries of the game state.<br /><br />Generally speaking, classes (and enums) should reflect only the rules of the game and be independent of the set of cards, which should come from separate and interchangeable data files. This ensures that <br /><ul class='bbc'><li>you can replace or delete cards freely</li><li>you don't have hacks in some card that bypass the rules of the game and cause problems</li><li>you can test, and reason about, a closed set of simple building blocks rather than an unlimited number of cards containing arbitrary code</li></ul><br />Do you need types of effect other than triggers? Can you tell more about the rules of your game?<br />[/quote]<br /><br /><br />

Thanks, this seems like it will work just fine. I am going to go brush up on my XML.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this