architecturesoftware-designstate-machine

How to architect a MtG-like card game


I want to make a card game similiar in concept to Magic the Gathering, or as a smaller example - Love Letters. Both games feature a lot of different cards, with different effects. For example (simplified):

My current idea is to make card objects to listen for every in-game action/state change and produce effects for respective players, and effects would serve as property modifiers for players. But: that requires to give each card the knowledge about almost the whole game, and overall seems like an overcomplication to me.
Also, I've considered to place all this code in the "Game" object, since it is a whole game context, aware of every player, every card, every effect. But: that seems even to be even worse, as it makes "Game" a Godlike-Object with thousands of lines of code.

TLDR: it would be nice to find some source code that deals with similar concepts in elegant way. But even better - some article that also gives some answers about "why this way" and "how this is better"


Solution

  • I lack basic understanding of the main mechanism of the game you intend to implement, so I could be completely wrong (let me know if that's the case), but here's my understanding of your basic needs, in very abstract terms:

    In order to properly implement this, you will need to implement a mapping between types and the collection of cards in play for the given type, let's call this 'cardTypeMappings' and you would be able to easily get all the cards of the given type by getting the collection corresponding to that type from cardTypeMappings. This would be initialized on game initialization, as you know what card types exist and you either assign an empty collection to each time at start if there are no cards yet at play or so.

    When a card gets into play, it must make its way into this mapping and when the card is no longer at play, it must be removed from the mapping, so this mapping will always have realtime information of what cards are at play, partitioning them to their corresponding types.

    Now, each card object needs to have an owner, that is, a reference to the actor.

    Now, each action has an action type and you need to have a mapping between action types and card types, let's call it actionTypeCardTypeTriggerMap. Similarly, each event should have its own mapping, let's call it eventTypeCardTypeTriggerMap and the event should also have a t.

    Now, if an action occurs, then we know what the action type is, hence we can do something like this:

    foreach cardType in actionTypeCardTypeTriggerMap.get(action.type)
        foreach card in cardTypeMappings.get(cardType)
            card.handle(action)
        end foreach
    end foreach
    

    and in case of events, similarly:

    foreach cardType in eventTypeCardTypeTriggerMap.get(event.type)
        foreach card in cardTypeMappings.get(cardType)
            card.handle(event)
        end foreach
    end foreach
    

    I recommend the creation of an Engine class or something of the like where these loops would be inside an action or event handler that is triggered by the event or action at play.

    Now, how should the different card types be implemented?

    I strongly recommend the implementation of a BaseCard abstract class where you implement every handler that are to be handled in the same manner for all card types, but where you only declare without implementing the handlers which differ for each card.

    Then you can extend your card types from BaseCard and implement/override whatever methods need further specification for the type.

    Similarly for action you could create a BaseAction and for event you could create a BaseEvent class and both would have at this level the mechanism that triggers Engine's loop when an action or event occurs. And then your game object would not worry of the cards properly handling actions and events, it would just call the corresponding action and/or event methods. Of course, drawing a card is an event or action, depending on your definition too.