Roguelike AoC – part 3
A third lesson of roguelike game tutorial is about adding bouncing off the walls. Of course, it won’t be final solution, but a first step towards it.
From the very beginning of this lesson I’ve made changes to the resulting code. First of all – I’ve left the handleInput method untouched – in the previous lesson I’ve decided that this method should not know a thing about the internals. Its only job is to know which method to pass flow to, when a specific key was pressed. Therefore, new method checking the position will be called directly from playerMove, although it also needed additional changes. Instead of directly (without checks) changing player location, I’ve also introduced intermediate variables that store possible new location of a user (or possibly a creature in the future – method should be transparent about that).
After assigning new possible values to the location variables there is a method called – canGoToPosition – that in original tutorial besides x and y values, also accepts Player instance. For the time being I’ve decided to omit that. I expect that in the future, it would be wise to pass this object – it’s possible for some creatures to go into specific locations and for some not (eg. water or fire).
What I also did differently is that I do not hardcode allowed locations to go into in a switch statement, but I’ve prepared an array containing all the locations that creature/player is actually allowed to go. In the future there might be unallowed places or dubious places arrays, with no-go terrain or with locations that needs special abilities/items (like aforementioned water or fire).
What was a learning moment was this.
const int ALLOWED_LOCATIONS_SIZE = 1;
char allowedLocations[] = {'.'};
As I’m using C99-compliant compiler, there is no problem with creating variable-length array. However, when I’ve tried this approach:
const int ALLOWED_LOCATIONS_SIZE = 1;
char allowedLocations[ALLOWED_LOCATIONS_SIZE] = {'.'};
The compiler actually raised compilation error with variable sized array may not be initialised error. After some googling I’ve found out, that there is no problem with actually defining an array with variable size, however it is not allowed to actually initialise variable-length array with a specific amount of elements! Lessons learned, I like it.
To make the code more modern (welcome home C99 standard) I’ve also added bool datatype to indicate the return value of canGoToPosition method. Returning boolean value increases readability and clears the intent if it.
Code
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <curses.h> typedef struct Player { int xPosition; int yPosition; int health; } Player; enum Direction {DIRECTION_NW, DIRECTION_N, DIRECTION_NE, DIRECTION_W, DIRECTION_E, DIRECTION_SW, DIRECTION_S, DIRECTION_SE}; int screenSetup(void); int mapSetup(void); int handleInput(char, Player* user); int playerMove(Player* user, enum Direction dir); int playerDraw(Player* user); bool canGoToPosition(int y, int x); Player *playerSetup(void); int main() { Player *user; // There is a warning here (-Wunused-but-set-variable) - that will be char keyPressed; screenSetup(); mapSetup(); user = playerSetup(); /* Main game loop */ while((keyPressed = getch()) != 'q') { handleInput(keyPressed, user); } endwin(); return 0; } Player *playerSetup(void) { Player *newPlayer = malloc(sizeof(Player)); newPlayer->xPosition = 14; newPlayer->yPosition = 14; newPlayer->health = 20; playerDraw(newPlayer); return newPlayer; } int screenSetup(void) { initscr(); printw("Roguelike game!"); noecho(); refresh(); return 0; } int mapSetup(void) { int x = 13; int y = 13; mvprintw(y++, x, "---------"); mvprintw(y++, x, "|.......|"); mvprintw(y++, x, "|.......|"); mvprintw(y++, x, "|.......|"); mvprintw(y++, x, "|.......|"); mvprintw(y, x, "---------"); x = 30; mvprintw(y++, x, "---------"); mvprintw(y++, x, "|.......|"); mvprintw(y++, x, "|.......|"); mvprintw(y++, x, "|.......|"); mvprintw(y++, x, "|.......|"); mvprintw(y, x, "---------"); x = 13; y = 25; mvprintw(y++, x, "------------"); mvprintw(y++, x, "|..........|"); mvprintw(y++, x, "|..........|"); mvprintw(y++, x, "|..........|"); mvprintw(y++, x, "|..........|"); mvprintw(y, x, "------------"); return 0; } int handleInput(char keyPressed, Player *user) { switch (keyPressed) { // move up case 'w': case 'W': playerMove(user, DIRECTION_N); break; // move left case 'a': case 'A': playerMove(user, DIRECTION_W); break; // move right case 'd': case 'D': playerMove(user, DIRECTION_E); break; // move down case 's': case 'S': playerMove(user, DIRECTION_S); break; default: break; } return 0; } int playerMove(Player *user, enum Direction direction) { int possibleX = user->xPosition; int possibleY = user->yPosition; 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) } // Cleanup of current position mvprintw(user->yPosition, user->xPosition, "."); if(canGoToPosition(possibleY, possibleX)) { user->yPosition = possibleY; user->xPosition = possibleX; } // We redraw the player no matter if the position changed or not playerDraw(user); return 0; } bool canGoToPosition(int y, int x) { const int ALLOWED_LOCATIONS_SIZE = 1; // Hard-coded to simplify iteration loop char allowedLocations[] = {'.'}; char locationToCheck = mvinch(y, x); for(int i = 0; i < ALLOWED_LOCATIONS_SIZE; i++) { if(locationToCheck == allowedLocations[i]) { return TRUE; } } return FALSE; } int playerDraw(Player* user) { mvprintw(user->yPosition, user->xPosition, "@"); move(user->yPosition, user->xPosition); return 0; }
Leave a Reply
You must be logged in to post a comment.