Sign in to follow this  
napalmb

[C++] function to replace chars after moving.

Recommended Posts

napalmb    122
Hello, fellow devs. I'm having a slight hangup with the character movement in my console ASCII game. What I'm trying to do is that when the players presses a movement key (WASD or arrow), a function reads the information about the character in the direction that player is heading, storing that information in a struct. If the title is "safe," such as a blank space, then move to that position. Additionally, the previous position should be replaced with whatever character was there (not the player). However, I'm having a bit of trouble working out the logic behind this. This is what I've managed to produce this far, with the help of a few examples I found lying around the internet:
//store information retrieved from getxy
struct storeinfo
{
	char	textread;
	colors	back;
	colors	fore;
};

//find character and attributes at a give position
//store information in storeinfo struct
void getxy( int x, int y, storeinfo *store )
{
    COORD position = {x,y};
	ReadConsoleOutput( hStdOut, &buffer[ 0 ], bufferSize, position, &windowSize );
	
 //int x = px,y =py;
 //char behind = buffer[x+SCREEN_WIDTH*y].Char.AsciiChar;
	store->textread = (char)buffer[x+SCREEN_HEIGHT*y].Char.AsciiChar;
	store->back = (colors)(( buffer[x+SCREEN_HEIGHT*y].Attributes & 0xf0 ) >> 4 );
	store->fore = (colors)( buffer[x+SCREEN_HEIGHT*y].Attributes & 0xf );
}

if ( keyPressed == 'w' || vkeyPressed == VK_UP)
            {
                if (py>0)
                {
                    //gotoxy(px,py);
                    //cout<<' ';
                     getxy(px,py,ontop);
                     gotoxy(px,py);
                     cout<< ontop->textread;
                     
                     getxy(px,py-=1,ontop);
                    if(ontop->textread == ' ')
                      {
                    //setColor(ontop->back,ontop->fore);
                      gotoxy(px,py-=1);
                      cout<<ascii;
                      }


                }
            }

However, I can't make heads or tails of the output. I've tried a few other combinations of function calls, but I can't seem to spot what I am doing wrong. I'm guessing its more of a logical issue than a codical one, dor lack of a better term.

Share this post


Link to post
Share on other sites
Zahlman    1682
0) Try to indent your code more consistently, and come up with better names for things. A struct should have a "noun" sort of name, because it's a thing, not an action.
1) The natural way to get information "out of" a function is to return it.
2) Try to restrict the scope of your variables. I can't imagine why your "buffer" would be global.
3) If you read the documentation for ReadConsoleOutput, you see that you can use it to read only a small chunk of the display at a time - in fact, you can just read a single location, directly into place. Actually, the bug was that

Quote:
dwBufferCoord [in]

The coordinates of the upper-left cell in the lpBuffer parameter that receives the data read from the console screen buffer. The X member of the COORD structure is the column, and the Y member is the row.


You were trying to read the data in such a way that it would copy the 'position' to the top-left of the buffer, and proceed from there.

4) In C++, use C++-style casting.

5) You don't need or want to repeat all the checking code in each of the movement cases. In each movement case, calculate the "desired" position, and call a function to check if movement is possible.

6) Be very careful about tricky things like calling a function with 'func(py -= 1)'. The value of 'py' is now decremented for the purpose of all the following code, regardless. You almost always want 'func(py - 1)', instead; if needed, make the change 'py -= 1;' explicit, in its own statement.

7) But you really don't want to actually read the screen for something like this. You want to remember what's where in your own data structure, and draw *from* the data structure.

Share this post


Link to post
Share on other sites
napalmb    122
Thank you for replying, Zahlman. I'll try to touch on each point individually:

0. I know it looks weird. I had to switch from CodeBlocks to DevC++ for a few days, and DevC++'s idea of indenting is to throw stuff halfway across the screen, and I'm a bit lazy with the space bar. I take more care to format code I post in the future.
1.I didn't know it was necessary or even possible to do a return statement in a void function.
2. The buffer is global because I thought there was only one buffer that everything get written to. Maybe I am misunderstanding the concept?
5. Thats something I'd like to get done in the near future, but I'd like to get it working first.

As to the rest of your points, I'm not quite sure how to fix those. I'm trying to learn stuff pretty much on the fly, so it is very likely to make many mistakes along the way. Would you mind very much showing me how you would go about fixing this mistakes, as I still can't see it.

Share this post


Link to post
Share on other sites
Zahlman    1682
Quote:
Original post by napalmb
1.I didn't know it was necessary or even possible to do a return statement in a void function.


It is possible to use 'return;' in a void function. It is not possible to return a value from a void function. But guess what? It's your function, so you get to say what the return type is. And when you need to get information out of a function, the first way you should consider doing it is to return it. You know what kind of information you need to get back, and therefore you can set the return type accordingly.

Quote:
2. The buffer is global because I thought there was only one buffer that everything get written to. Maybe I am misunderstanding the concept?


Restrict scope as much as possible. The reason for making something global is because it needs to be reused by different functions, such that one function can "see" what another function did to the variable. This is usually the opposite of what you want: isolate things and let each function have its own scratch space. And even when you do want to "communicate between" the functions: just as the natural way to get information out of a function is to return it, the natural way to put it into the function is to use a parameter.

Quote:
5. Thats something I'd like to get done in the near future, but I'd like to get it working first.


It will be easier to get it working if you address this first. For a very simple reason: once you figure out how to make it work, you won't have to go back and clone the code for each case, make adjustments and then test to make sure you didn't typo on those adjustments.

Quote:
As to the rest of your points, I'm not quite sure how to fix those. I'm trying to learn stuff pretty much on the fly, so it is very likely to make many mistakes along the way. Would you mind very much showing me how you would go about fixing this mistakes, as I still can't see it.


(7) in particular is a design issue.

Let's say we have an array somewhere that has one element for each screen location. It needs to represent whatever "is there" in the game world, so it has some kind of "terrain" type. You can probably count the possible types of terrain easily; so let's use an enumeration for that. We also remember the x and y position of the player. We can make a struct to represent the "map":


enum terrain_t {
LAND,
OCEAN,
BARRIER,
// well, I don't know what kinds of terrain you have?
};

struct map {
terrain_t terrain[80][24];
int player_x, player_y;
};


To draw the map, we iterate over the terrain array, drawing each symbol; then we go to the player's location and overwrite the terrain with the player's symbol. Notice that the player is not part of the terrain!


void draw(const map& m) {
gotoxy(0, 0);
for (int j = 0; j < 24; ++j) {
for (int i = 0; i < 80; ++i) {
cout << m.terrain[i][j];
}
}
gotoxy(m.player_x, m.player_y);
cout << '@'; // or whatever symbol you're using for the player
}


To move the player, we first check if the move is legal, by looking at the corresponding location in the terrain array. If it's clear, we then change the player_x or player_y value accordingly. This is left as an exercise. Hint: What questions do you need to answer in order to tell the player to "move"? What kind of data is that?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this