How I Made Card Effects Work Using the Strategy Pattern


Since a card effect can basically be anything, the biggest hurdle was writing the code in a way that's reasonably readable, with an emphasis on expandability. I need to be able to add new effects easily.

The obvious solution was to use the strategy pattern. For those unfamiliar with it, here's a great video on the subject. In short, the strategy pattern lets us select an algorithm at runtime (fancy word for "on the fly") for handling a certain object. Because a card can have any effect, this was the approach I needed.

So basically, every effect is a separate "strategy" that can be plugged into a card. I just write a bunch of these strategies and plug them into cards. Adding new effects just becomes adding different strategies. Sounds simple enough, right? RIIGHT?? Well, not quite. Since an effect can affect ANY object, we need a way to tell the effect which objects to target (for now, fields and monsters, but I plan to expand this).

To tackle this problem, I have special "phase" objects. These phase objects receive signals whenever a field, card, or monster is selected, and distribute them to the selected effect. What does this mean? Well, the flow goes like this:

  1. You select a card, and the phase object remembers which card you selected. Say it's the card that adds 100 strength to the monster.
  2. You select a monster token. The phase object receives the monster you selected and passes it to the previously selected effect.
  3. The effect then performs whatever it needs to perform on the object.
  4. But what if I select a field?? (not a step, but I bet someone was thinking about this)

public interface Effect

{

    // Return true if it can add a thing

    bool TryToAddThings(params object[] thingToAdd);

    // If the effect can be executed, return true

    bool IsReadyToExecute();

    // Execute the effect

    void Execute();

}

Thanks to params object[], the bool TryToAddThings(params object[] thingToAdd) method can take any number of objects of any kind. So what we do in each effect is check if the objects passed are the ones we need and, if not, reject them.

Good, now all we need to do is to execute them. Well, this is the easy part. In the game loop (or rather the main _Process method) I go through all the added effects, check if they can be executed with IsReadyToExecute(), and Execute() the ones that can be.

So yeah, that's basically the shortened version. If you have any questions, feel free to post in the comments.

Get UntitledCardGame

Leave a comment

Log in with itch.io to leave a comment.