Roguelike AoC – part 15
In the fifteenth lesson of a roguelike game tutorial things get more exiting – we fight!
Small changes required
As usual with adding new file (in this case – combat.c) besides creating a new file please remember to add it to the CMakeLists.txt file in order for it to be build.
Usually when it comes to some boolean flag I’m not following author’s convention to use int to represent it. I use bool type to indicate that kind of properties. I’ve used it for playerAttack information in a combat function and for alive flag in a Monster struct. Don’t forget to also use bool for setting the value of alive in killMonster function:
int killMonster(Monster *monster) { mvprintw(monster->currentLocation->position.y, monster->currentLocation->position.x, monster->currentLocation->displaySign); monster->alive = FALSE; return 0; }
Do I move or do I fight?
With my already existing code I had to actually think for a moment – where to actually put my ‘handling fighting’ code. As You recall I’ve extracted separate function to handle input and then forwarded the directions keys to playerMove function. The thing is – right now the playerMove accepts a direction and a player. We do not have any information about the monster that actually lurks nearby and tries to kill us.
To solve this, I’ve created the function mentioned by the author – getMonsterAt. Of course, I had to tweak it to work with my Location structures but that was easy.
Monster *getMonsterAt(Location *location, Monster **monsters) { for (int x = 0; x < NUMBER_OF_MONSTERS; x++) { if ((location->position.y == monsters[x]->currentLocation->position.y) && (location->position.x == monsters[x]->currentLocation->position.x)) { return monsters[x]; } } return NULL; }
However, I still did not have information about monsters in the dungeon. Therefore, I had to change signature of the function playerMove and handleInput to actually accept Level variable (containing our player pointer). It required a lot of changes in variables usages, so I won’t be posting them here – just note that it was a lot of replacing 😉
Finally, in order to make an attack I had to extend the IF statement in the playerMove function. So right now besides canGoToPosition I had to create separate isMonsterOnLocation function (very similar to the one above – I ‘ve skipped the ideas of refactoring it for the time being) to have actual information if the target location contains a monster.
bool isMonsterOnLocation(Location *location, Monster **monsters) { for (int x = 0; x < NUMBER_OF_MONSTERS; x++) { if ((location->position.y == monsters[x]->currentLocation->position.y) && (location->position.x == monsters[x]->currentLocation->position.x) && monsters[x]->alive == TRUE) { return TRUE; } } return FALSE; }
Armed with such possibilities I’ve added a new condition to the playerMove function.
else if(isMonsterOnLocation(&level->locations[possibleY][possibleX], level->monsters) == TRUE) { // Player always attacks first combat(level->user, getMonsterAt(&level->locations[possibleY][possibleX], level->monsters), TRUE); }
Build, run and viola! It works, and we can kill monsters. Feels good 😉
Code
Here is the direct link to the GitHub repo.
Leave a Reply
You must be logged in to post a comment.