3
\$\begingroup\$

Based on Design of a turn-based game where actions have side-effects

I am building a card-game where cards can have side-effects be triggered under different circumstances.

I was given a good idea on how to handle different phases by having hooks (events) that different cards can be triggered to do something, like when a card is played, draw an additional card, etc.

I am not sure how I should compose all these things to work together, I have a GameState object which handles the state of the game (who's turn, player objects, timers) checking if a move is legal etc.

I am having a Card class that currently just has a type and a name getter/setters and properties. I believe the best way to store cards would be to use .JSON files, because that would let you simply write a new card.

To achieve side-effects I am thinking of having "components" that is configured through a cards JSON-file.

{
    "name": "Ocean Humans",
    "type": "Creature",
    "components": {
    }
}

However, I am not sure how I should compose these JSON-files to some actually working object. How do I call these components when I invoke a hook_event (draw a card, phase begin). And make sure right components are run?

\$\endgroup\$
  • \$\begingroup\$ What are you looking for more precisely ? \$\endgroup\$ – Emadpres Jul 25 '15 at 13:18
  • \$\begingroup\$ Well, how would I create the objects with there components, how do I connect a component call when triggering event, not sure how to compose these things \$\endgroup\$ – Salex Jul 25 '15 at 15:18
1
\$\begingroup\$

I would create a bytecode interpreter for this, see: http://gameprogrammingpatterns.com/bytecode.html for a lot of details. Keep in mind that the bytecode is an example, but you can easily write something similar on your own using more readable instructions. The basic idea is the same though.

So you write a bunch of small programs for each card effect. Then for each small component an events should be triggered from your gamecode. In your card JSON you could create the various available hooks with the corresponding bytecode to that hook. For example:

{ "name":"Healing Potion", "description":"When a friendlycard is damaged, ignore the damage. Then this card is removed from play", "components":{ "friend_is_damaged":"bytecode", } }

The bytecode is executed when the event "friend_is_damaged" is thrown. The bytecode has the instruction "remove myself", which in turn throws the event "friendly_card_removed". Any card that hooks to that event will then fire.

You could create a stack of events, if you want the effects to be resolved after completing all effects from one card, instead of fire each event immediately.

\$\endgroup\$
  • \$\begingroup\$ Wouldn't I need a way to subscribe these card events first to some stack or whatever so if multiple cards are played, they must be triggered in the right order, right? \$\endgroup\$ – Salex Jul 26 '15 at 9:10
  • \$\begingroup\$ Play card "One More", bind event draw_card, with some parameters "opponent_draws": true, etc. \$\endgroup\$ – Salex Jul 26 '15 at 9:17
  • \$\begingroup\$ You need to define all possible 'event' types. 'EndOfTurn', 'PointsScored' etc. your cards would listen to those events and run their own bytecode, which may in turn fire new events. Some events simply have no effect and won't react to those events. Or am I misunderstanding your question? \$\endgroup\$ – Felsir Jul 26 '15 at 11:25
  • \$\begingroup\$ This makes sense, thank you Felsir. Do you have any code example on this in a high level language like Python or JavaScript? \$\endgroup\$ – Salex Jul 26 '15 at 12:57
  • \$\begingroup\$ Sorry, I program in C# mostly. I hope these basic mechanics provide enough pointers to implement the mechanism. Good luck with your game! \$\endgroup\$ – Felsir Jul 26 '15 at 18:24
1
\$\begingroup\$

Consider adding scripting to your game. Then you can store code snippets in the scripting language in the cards themselves. That will give you a lot of flexibility. This could, for example, look like this:

{
    "name": "Ocean Humans",
    "type": "Creature",
    "description": "When Ocean Humans dies, you lose half of your hit points"
    "components": {
        "onDie": "this.owner.damage(this.owner.currentHitPoints / 2);"
    }
}

Options for scripting languages are:

  • Use any stock scripting language like LUA or Javascript. This is quite easy to do, because you can just use a library and you have a well-tested and optimized implementation of the language.
  • invent your own domain-specific languge. This will be lots and lots of work. It might pay off in the long run because you can optimize your syntax for your requirements. But be pessimistic in your cost/benefit analysis. Inventing and implementing a new programming language is no trivial task.
  • Or you can even use C# code you compile at runtime. This has the advantage that you can use your existing C# know-how.
\$\endgroup\$
1
\$\begingroup\$

I think the trick here is that a lot of cards have the same kind of effects with some variations while some cards have totally unique effects.

Imo, what you should do is to do this. This is written in typescript, but you can get the idea.

class Effect extends {

    static fromArray(game, card, data) {

        if (data.type == "SelfDestructEffect") {

            return new SelfDestructEffet(card, data.damagePerTurn);

        }

    }

}

class SelfDestructEffect extends Effect {

    constructor(game, card, damagePerTurn) {

        this.card = card;
        this.game = game;

        this.game.on("new-turn", () => { this.onNewTurn() });

    }

    onNewTurn() {

        this.card.damage(1);

    }

}

You create components that describes one effect of a card. You try to make them as customizable as possible so you can re-use them as much as possible between cards. You can still create "unique" components (i.e. that will only be used by one card) for really special effects.

Here is cards.json

{
    name: "Berzerker",
    type: "Human-beast",
    components: [

        {
            type: "self-destruct-effect",
            damagePerTurn: 1
        }

    ]
}

Note: The way I de-serialize the json to make the components isn't really scalable. If you're interested in how I would de-serialize the components, you can ask me, I'll write the code.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.