Adding split-screen multiplayer to c++ game

1.1k views Asked by At

I am coding for the NDS in c++ with libnds, but this question is not NDS-Specific. I currently have a text-based game in which the top screen just displays a logo, and you play on the bottom screen.

So I want to add a type of single-DS multiplayer in which one player plays on the top screen, and the other on the bottom. I dont have a problem with setting up the text engine with both screens, I just need to find a method of efficiently coding in multiplayer. Below I wrote a summary or simplified version of it.

Note: consoleClear() clears the screen and the only spot where the game stops is att the pause function.

//Headers

void display(int x,int y,const char* output))
{
    printf("\x1b[%d;%dH%s", y, x,output);
}

void pause(KEYPAD_BITS key) //KEYPAD_BITS is an ENUM for a key on the NDS
{
    scanKeys();
    while (keysHeld() & key)
    {
        scanKeys();
        swiWaitForVBlank();
    }
    while (!(keysHeld() & key))
    {
        scanKeys();
        swiWaitForVBlank();
    }
    return;
}

void pause() //Only used to simplify coding
{
    pause(KEY_A);
    return;
}

int main(void)
{
    //Initializations/Setup
    while (1)
    {
        if (rand()%2==1) //Say Hello
        {
            if (rand()%3!=1) //To Friend (greater chance of friend than enemy)
            {
                display(6,7,"Hello Friend!");
                display(6,8,"Good greetings to you.");
                pause();
                consoleClear(); //Clears text
                display(6,7,"Would you like to come in?");
                pause();
                //Normally more complex complex code (such as interactions with inventories) would go here
            }
            else //To enemy
            {
                display(6,7,"Hello enemy!");
                display(6,8,"I hate you!");
                pause();
                consoleClear();
                display(6,7,"Leave my house right now!!!");
                pause();
            }
        }
        else //Say goodbye
        {
            if (rand()%4==1) //To Friend (lesser chance of friend than enemy)
            {
                display(6,7,"Goodbye Friend!");
                display(6,8,"Good wishes to you.");
                pause();
                consoleClear();
                display(6,7,"I'll see you tomorrow.");
                pause();
                consoleClear();
                display(6,7,"Wait, I forgot to give you this present.");
                pause();
            }
            else //To enemy
            {
                display(6,7,"Goodbye enemy!");
                display(6,8,"I hate you!");
                pause();
                consoleClear();
                display(6,7,"Never come back!!");
                pause();
                consoleClear();
                display(6,7,"Good riddance!"); //I think I spelt that wrong...
                pause();
            }
        }
    }
}

I know gotos are confusing and can be considered a bad habit, but I cant think of a better way. My version of integrating multiplayer:

//Headers and same functions

int game(int location)
{
    switch (location)
    {
    case 1: goto one; break;
    case 2: goto two; break;
    case 3: goto three; break;
    case 4: goto four; break;
    case 5: goto five; break;
    case 6: goto six; break;
    case 7: goto seven; break;
    case 8: goto eight; break;
    case 9: goto nine; break;
    case 10: goto ten; break;
    default: break;
    }

    if (rand()%2==1) //Say Hello
    {
        if (rand()%3!=1) //To Friend (greater chance of friend than enemy)
        {
            display(6,7,"Hello Friend!");
            display(6,8,"Good greetings to you.");
            return 1;
one:;
            consoleClear(); //Clears text
            display(6,7,"Would you like to come in?");
            return 2;
two:;
            //Normally more complex complex code (such as interactions with inventories) would go here
        }
        else //To enemy
        {
            display(6,7,"Hello enemy!");
            display(6,8,"I hate you!");
            return 3;
three:;
            consoleClear();
            display(6,7,"Leave my house right now!!!");
            return 4;
four:;
        }
    }
    else //Say goodbye
    {
        if (rand()%4==1) //To Friend (lesser chance of friend than enemy)
        {
            display(6,7,"Goodbye Friend!");
            display(6,8,"Good wishes to you.");
            return 5;
five:;
            consoleClear();
            display(6,7,"I'll see you tomorrow.");
            return 6;
six:;
            consoleClear();
            display(6,7,"Wait, I forgot to give you this present.");
            return 7;
seven:;
        }
        else //To enemy
        {
            display(6,7,"Goodbye enemy!");
            display(6,8,"I hate you!");
            return 8;
eight:;
            consoleClear();
            display(6,7,"Never come back!!");
            return 9;
nine:;
            consoleClear();
            display(6,7,"Good riddance!"); //I think I spelt that wrong...
            return 10;
ten:;
        }
        return -1;
    }
}
int main(void)
{
    //Initializations/Setup
    int location1 = -1, location2 = -1;
    location1 = game(location1);
    location2 = game(location2);
    while (1)
    {
        scanKeys(); //Whenever checking key state this must be called
        if (keysDown() & KEY_A) //A key is used to continue for player1
            location1 = game(location1);
        if (keysDown() & KEY_DOWN) //Down key is used to continue for player2
            location2 = game(location2);
    }
}

Aside from this method being a bad practice, in the actual source code, I have hundreds of gotos I would need to add which would be too time consuming.

Any help is appreciated. If anyone has the slightest of a question, or answer, please ask/reply.

Edit: Though it is not preferred to do so, I am willing to rewrite the game from scratch if someone has a method to do so.

1

There are 1 answers

1
Nick Louloudakis On BEST ANSWER

Using if-else conditional statements for each case is a simple solution that comes first to mind.

For example:

int game(int i){
  if(i == 1){
    //first case code here.
  }
  else if(i == 2){
    //second case code here.
  }
  //....
  return 0;
}

The code in each case can even be put in other functions that will be invoked depending on each condition. This will probably be enough for your case.

A more flexible solution (but much more complex) is a dispatch table. The idea is to have separate functions with each desired functionality, and put pointers of them in an array. Then, you can call them by indexing the table, using those function pointers. This can be extremely helpful if you have a sequence of executions (function invokes) to be done and you want to set it done easily, or you want to have different results depending on your input, without changing your program.

There is an example below. This code can be used in C too, if you replace std::cout with printf and iostream with stdio library.

#include <iostream>

using namespace std;

// Arrays start from 0.
// This is used for code
// readability reasons.
#define CASE(X) X-1 

typedef void (*chooseCase)();

// Functions to execute each case.
// Here, I am just printing
// different strings.
void case1(){
    cout<< "case1" << endl;
}

void case2(){
    cout<< "case2" << endl;
}

void case3(){
    cout<< "case3" << endl;
}

void case4(){
    cout<< "case4" << endl;
}

//Put all the cases in an array.
chooseCase cases[] = {
    case1, case2, case3, case4
};

int main()
{
    //You can call each scenario
    //by hand easily this way:
    cases[CASE(1)]();
    cout << endl;

    //Idea: You can even set in another
    // array a sequence of function executions desired.
    int casesSequence[] = {
        CASE(1), CASE(2), CASE(3), CASE(4),CASE(3),CASE(2),CASE(1)
    };
    //Execute the functions in the sequence set.
    for(int i = 0; i < (sizeof(casesSequence)/sizeof(int)); ++i){
        cases[casesSequence[i]]();
    }

    return 0;
}

This will print at the output:

case1

case1
case2
case3
case4
case3
case2
case1