Roguelike AoC – part 10
A tenth lesson of roguelike game tutorial goes back to the topic of connecting doors. This time it’s the last lesson about the topic. The goal is to fix problems that appear on and on.
Final cleanups with constants becoming macros and ncurses
In the previous lesson we’ve extracted the code into separate files. What actually have happened, is that our constants with signs used to render locations were set as macros. Sudden problem arose with warnings being shown when passing them to ncurses display methods (like mwprintw). What I’ve finally ended up doing was:
- removed const modifier from getDisplaySignForLocationType function (in both declaration and body)
- made displaySign property in Location struct a char pointer
Now the code compiles without warnings.
New function of curses library
Before going into next subchapters I have to mention an existence of a function called unctrl. When we call curses functions that read something from the screen (eg. mvinch) it does not return char instance but chtype. That type is a special type in curses that holds not only the character on the screen but also additional attributes. Therefore, when we try to use its result to eg. compare it again plain char – we get a warning.
To fix that aforementioned function of unctrl is used. It takes a parameter of type chtype and return only a character associated with it.
Get rid of leaving floor sign when player goes (eg. through door)
In the lesson the solution was to scan the whole screen and read signs that are on it. With that information an two-dimensional array was filled with signs that are originally present at that location. My code, already having Location struct, does not need that. What it needs is for the Player structure to actually hold information about current location he’s at, and the previous one. Therefore, our existing methods could be handling this without referencing to the external information. From what I’ve read in the description of the next lesson – we will be creating some kind of wrap-up structure for the whole level. Maybe then I will have to change my approach. However, I think that even then this Level structure could actually benefit from storing my Location instances.
Below I present only changed playerMoved function. There was a change in Location struct – I’ve added currentLocation and previousLocation pointers and removed position completely.
int playerMove(Player *user, enum Direction direction) { int possibleX = user->currentLocation->position.x; int possibleY = user->currentLocation->position.y; switch (direction) { // move up case DIRECTION_N: possibleY -= 1; break; // move left case DIRECTION_W: possibleX -= 1; break; // move right case DIRECTION_E: possibleX += 1; break; // move down case DIRECTION_S: possibleY += 1; break; default: break; // We stay in place (usually 'turn' will pass then) } if(canGoToPosition(possibleY, possibleX)) { // Cleanup of current position mvprintw(user->currentLocation->position.y, user->currentLocation->position.x, user->currentLocation->displaySign); // We store info about the previous position (which is current one) user->previousLocation->position.y = user->currentLocation->position.y; user->previousLocation->position.x = user->currentLocation->position.x; user->previousLocation->displaySign = user->currentLocation->displaySign; // We switch the current position to the new one where we'll allow to go user->currentLocation->position.y = possibleY; user->currentLocation->position.x = possibleX; user->currentLocation->displaySign = unctrl(mvinch(possibleY, possibleX)); // We redraw the player no matter if the position changed or not playerDraw(user); } else { // When player 'stays' where he is then we have to move cursor 'back' to his current location move(user->currentLocation->position.y, user->currentLocation->position.x); } return 0; }
Get rid of leaving an additional sign when drawing connections
In the Youtube lesson this is left unresolved. However, I wanted a nice, clean map being rendered so, I’ve decided to take care of it. The main problem actually lies in a connection generating algorithm. My code has a problem that author’s code actually does not have. Sometimes (depending on which doors were chosen to be connected), there’s no way for the algorithm to proceed further – there are obstacles everywhere (that cannot be used to go) or chosen direction is not applicable.
This can be easily illustrated by an example. Let’s say there’s a room on the right part of the screen, and also having a door on the eastern wall. This door was chosen to be connected. Then a second door is chosen in a different room that lies on the left. When our algorithm tries to connect door on the right to door on the left it first tries going up. It cannot go there as there’s a wall. Then tries right direction – also there’s no way as that would increase the distance between the doors. For goind down the situation is the same like with going up – there’s a wall, and we cannot allow it. Finally, there’s only going left – unfortunately there’s a floor there, and we not allow to go when there’s floor. Why, You may wonder? Good question!
It seems that fixing that bug is trivial – just add floor to the allowed locations our algorithm can go through! Unfortunately that may fix a part of them problem – as we go left through the first room, we still probably will hit the wall on our way. We could get lucky when there’s a door on that wall – algorithm might use them, but that’s not granted.
Taking everything under consideration I’ve decided to go for it – so enable using floor as allowed location. However, for the ‘power’ of connecting algorithm I’ve decided to leave it as it is. I’m doing this whole project in order to get some familiarity with C language, not to create full-blown RPG game.
// example of changed condition for going left if ((abs((temp.x - 1) - doorTwo.x) < abs(temp.x - doorTwo.x)) && ((mvinch(temp.y, temp.x - 1) == *EMPTY_SIGN) || (mvinch(temp.y, temp.x - 1) == *FLOOR_SIGN))) { previous.x = temp.x; temp.x = temp.x - 1; }
Get rid of leaving an additional sign when drawing connections
That was also leaving the map in some inconsistent state sometimes. Although, it was fairly simple to fix – due to the fact that I’m passing Location variables and not just positions to the connectLocations function it is only a matter of additional assignment statements. I won’t post the whole method here – You can see it in the ZALINKUJ.
Below You can find an image showing (with the red circle, pardon my poor designer’s skills 😉 ) that the algorithm was fixed in this regard.
Code
Here is the direct link to the GitHub repository.
One Comment
Leave a Reply
You must be logged in to post a comment.
Roguelike AoC – part 11 – Bare.Metal.Dev
[…] the previous lesson I’ve actually fixed a couple of bugs. However, I did not implement the solution that the […]