0
\$\begingroup\$

I have been working on a RPG where the player is free to wander an open world but when a monster is encountered or on keyboard command the game switches to a turn based system. Note this is not like final fantasy like games, I intend for the turn based combat to take place in the same map, there is no scene transition.

I am trying to wrap my head around how to implement the turn based combat system. Right now I have a basic game loop that handles the main world map where the player can move above, pick up items, engage in dialogue with npcs, this looks like this:

while (does_not_quit){
        input();
        logic();
        render()
        }

Within logic() there is a keyboard command that switches the game to turn based mode, turn based meaning the player and npcs' movement and actions are now controlled by aciton points and these refresh once per turn. My question is, mechanically from a software design point of view, what is the best way to implement this turn based system on top of the main game loop?

Currently, what I do is, upon entering turn based mode, I start to check player movement against available action points which are read from a json file for each npc (and player). But how do I generate the effect of turns? For example, when the NPCs move I have to halt all commands from the player and I want each NPC to move separately, one after another and then when its the player's turn again, keyboard commands are restored and the player makes his moves before hitting "end turn". Naively, I have tried using a for loop to cycle through all the npcs+players in a given turn but this doesn't seem right?

I have played around with a few methods but it just doesn't feel right and I feel that there must be some classic design patterns that do this but I haven't been able to find any literature on this.

Can anyone advise?

\$\endgroup\$
6
  • \$\begingroup\$ Here’s an blog post about a turn based game loop: journal.stuffwithstuff.com/2014/07/15/a-turn-based-game-loop It handles different turn rates for different entity types, which you may not need/want, but leaving that part out should be easy enough. \$\endgroup\$
    – Ryan1729
    Nov 23, 2021 at 6:25
  • 1
    \$\begingroup\$ The abstract is that it will be like a pause, except there is no resume button. Instead, while it is paused, you are going to put all the things that want to move in a data structure, pull them one by one, and animate their motion. Then the game resumes. And you would need a flag to know if the game is supposed to go into that state again "at the end of the turn". The details depend a lot on how your engine architecture looks like (is it OO, ECS, what?) and how exactly you want the mechanic to work (is the structure just a queue, or are there priorities or complexities?, how a turn ends?). \$\endgroup\$
    – Theraot
    Nov 23, 2021 at 11:20
  • 1
    \$\begingroup\$ Sounds like you need a State Machine where the state tracks where you are inside the current turn, and once on each game loop frame you tick the machine's current state, potentially advancing to the next state when that state's actions are completed or its criteria (like waiting for a click or a delay) are fulfilled. \$\endgroup\$
    – DMGregory
    Nov 23, 2021 at 12:47
  • \$\begingroup\$ I could explain how to solve your problem using state machines, but first I would need to know if your main development paradigm to make sure we speak the same language. Object oriented? Procedural? ECS? \$\endgroup\$
    – Philipp
    Nov 23, 2021 at 14:44
  • \$\begingroup\$ @Philipp, I have been learning by following the tutorials from parallelrealities.co.uk/tutorials/rogue. I believe this counts as OO. The model used in that tutorial is not what I want, I do not want all the entities to move simultaneously, but in sequence. Theraot: How could I achieve a pause? The idea is that at the beginning of combat all characters are sorted by initiative and perform actions accordingly. At the end of turn they are sorted by initiative again (which could have changed due to actions). Thus the order in which the combatants act is not the same per turn. \$\endgroup\$
    – lancen
    Nov 23, 2021 at 22:06

1 Answer 1

1
\$\begingroup\$

When your game has different modes it's in, and each mode should have different logic and different UI, then that's often a use-case for the state machine pattern.

So instead of having all your game logic in your one logic()-function, you create a separate logic-function for each state your game can be in.

You might also want to create an enter-function and leave-function for each state which do the logic for initializing and cleaning up the state. In order to keep everything that belongs to one state together, it can be a good idea to keep those three functions for each state together in one source file for each state. Or if your code gets too long for that, you might want to create a separate folder for each state.

Your logic-function would then look something like that:

void logic(void) {
    /* stuff you want to happen in every state */

    case (currentState) {
        STATE_WALK:
            walk_logic();
            break;
        STATE_DIALOG:
            dialog_logic();
            break;
        STATE_COMBAT:
            combat_logic();
            break;
    }
}

Now you can switch states by changing the value of currentState between a bunch of constants. But wait, remember to call the enter and leave functions. To make sure you don't skip them, you might want to create an utility function for that:

void switchState(int newState) {
    // exit the old state
    case (currentState) {
        STATE_WALK:
            walk_leave();
            break;
        STATE_DIALOG:
            dialog_leave();
            break;
        STATE_COMBAT:
            combat_leave();
            break;
    }
    // enter the new state
    case (newState) {
        STATE_WALK:
            walk_enter();
            break;
        STATE_DIALOG:
            dialog_enter();
            break;
        STATE_COMBAT:
            combat_enter();
            break;
    }

    // NOW we can change the state
    currentState = newState;
}

Note that you can also have more than one state-machine in your game. For example, your function combat_logic might in turn be implemented by a state machine itself switching between states like "select unit", "select unit action", "select action target", "execute action", "execute ai" etc. This pattern of state-machines within states of other state-machines is called "Hierarchical State Machines".

\$\endgroup\$
3
  • \$\begingroup\$ What you describe is similar to what I have. I think the issue is that the logic and drawing step are happening one after another. It appears like all the entities move at the same time. In order words, when I press "up_key", it moves the player and then all the NPCs move one step in lock-step. It's like I want to render one logic step at a time instead of rendering everything in a loop after all the logic has been processed. Something like: move_player->render->move_npc_0->render->etc, to give the effect to the viewer that each entity moves sequentially. Does that make sense? \$\endgroup\$
    – lancen
    Nov 26, 2021 at 16:19
  • \$\begingroup\$ @lancen I don't know if you've solved the timing issue of your characters to move independently, and not at the same time. I had a similar issue yesterday and made a simple fix to it. You might want to add a small time-offset (random for each character, or fixed offset for all) for when each character should start moving. By having an offset of 0.0 for the first character, 1.0 for the next character (and so on), they will move independently on their own. The speed of them all can also vary to give more illusion of a random movement pattern. \$\endgroup\$ Mar 8, 2022 at 12:49
  • \$\begingroup\$ @Daniel_1985, I recently did. I don't know if what I did is correct but my solution was to create an "action queue". When a character moves, the action is not immediately executed, it's pushed into a queue, the queue is then pushed out one at a time at the level of the main game loop, the camera is set to follow the actively moving entity and the "illusion" of moving the characters sequentially is therefore created by setting a SDL_Delay after each action executed. The issue before was I used a for loop and the entire loop would execute in a SINGLE iteration of the main loop. \$\endgroup\$
    – lancen
    Mar 8, 2022 at 22:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .