C++ Interactive Fantasy Novel, Intro to CS II Final

8:04 PM

Spring quarter is over, and I'm excited to talk about the projects I worked on! This quarter, I took two intensive CS courses: Computer Architecture and Assembly Language, and Intro to CS II, done in C++. Our final project in Intro to CS was to create a text game.





Requirements

 Our requirements list was a chaotic mess, but I've boiled it down to:

  • Text-based game
  • Player must have a goal
  • Player must gather items to achieve the goal
    • One or more of these items must be required for the solution
    • Player must have some kind of container in which to store items
      • The container must be somehow limited
  • Player must move through a series of rooms/spaces
    • Must use an abstract Space class with pure virtual functions
    • Must have at least 6 spaces of at least 3 different "types" of spaces
      • ie, must have at least 3  different classes derived from the parent Space class
    • Each space must have at least 4 pointers to other spaces
    • Some may be Null, such as if you are in a room with only 1 door
    • Each space must have a special action/ a way for the player to interact with and change the status of the space
      • ie, turning on the lights
    • Must keep track of where the player is
    • There must be a "time limit"
      • this does not need to be measured in actual time
  • Must have pre-set input
    • ie, menu options
  • All other details are open to interpretation and are design decisions

I decided to implement a short, fantasy adventure game/interactive novel. I only had about a week and a half to finish the entire project, so the result is simple. My goal was to finish the project and meet all requirements, but build a good foundation for expansion. I have lots of ideas and do plan to expand my game over the summer. I was able to accomplish the completion of the game on time with 100% on the assignment.

In the following walk-through of the game, I'll demonstrate functionality, as well as explain my design decisions. After that, I'll discuss things I wish I'd done differently, changes that I want to make, and some general reflection.

The game itself:


The game essentially has two tracks: you win or you lose. You can lose by not having collected the key item, or by exceeding the "time limit"

You begin at the door of a ruin. The flavortext provides background on your character and motivation:





You have entered the first room and now have the options:


Option 1: meets the interaction criteria, allows you to illuminate the room




Option 2: prints the contents of your inventory. I chose to implement the "container" requirement by giving the Player class a vector, which can be populated with pointers to Item objects. Each room has its own vector with pointers to Item objects.

Option 3: moves you to the next room


I decided to simplify motion. You progress linearly through the game, and are unable to move back. While designing my game, I developed a map of the setting. The implementation only partially reflects how I imagine the setting looking, as you cannot really explore, only move forward. This is something I want to expand in a second version of the project.

You'll notice that each time you select an option you get a status message about the ruin falling apart. As a way to meet the "time limit" requirement, I implemented a ceiling of 10 actions per room. I find that it adds no value to the experience, but it does tick that requirement box. I could have implemented it such that a challenge is presented where you need to carefully select what actions to take, but that isn't the atmosphere I wanted this story to have. Every 3-4 actions, the status message becomes more urgent, until the whole place collapses.

All Rooms are objects. I have a base class called Room, which all of the individual room classes derive from. The derived classes inherit the virtual functions, as well as several variables that are constructed, set, and got by the Room class. For example, the name of the first room is Entry.

Room 2:


You are presented with a similar list of options. One difference is that the polymorphic interact function  allows you to take a different action in this room. You can see that instead of lighting torches, you can turn off a stove.

















Additionally, you can begin searching for items here. Items are all objects, and are implemented similarly to Rooms/spaces. There is a base class from which all Item types derive, which has constructors, a destructor, and initialization, getting, and setting of common variables like name and value.

My system for Item collection is, admittedly, clunky. I'll talk more about how I want to change it later in this post. As of this post, the player can only collect all or none of the items. If the player decides to collect, the room's vector is cleared, and the player's vector is then populated with pointers to the Items that were in that Room.

Here you can see that selecting option 2 prints a list of the Items and their values, and an option to collect them or not.

I selected Yes, and now have the Items in my Inventory.

Here you can see that selecting option 3 prints the inventory, while also providing a brief description.



Text at the bottom displays how many slots you have left. There aren't enough Items to fill up the slots. This is simply in place to meet the requirement.


Room 3


This room's interaction is to have a chat with a rat who lives there




Again, we can collect Items, and have them added to the player inventory vector


Room 4:

There is a bit of foreshadowing here




Interaction


Items



Inventory snapshot:


Room 5


Selecting Option 1 here yields the key item needed to win. It's a key that grants access to the final room. I decided to have the key hidden in the interaction choice instead of the Item choice since it was a bit less obvious, and less easily glossed over.
Collecting the keys allows you to find out whose pink lanyard this is

Final Room:
Without the KEYS from the previous room, you cannot enter this one.
If you leave Lab 2 without the KEYS, the game ends and you lose


Otherwise

You are forced into an encounter with the Dragon. The Dragon does not exist as an object, but is simply lines of dialog. Given that I have a base Character class, from with the player derives, creating the Dragon class with a dialog tree would have been trivial.



Once you've finished your conversation with the Dragon, it is announced that you have won, and your inventory is printed.


It seems, perhaps, pointless to have the collection of items available as an option when the only true win condition is the key that opens the room to the dragon. To that I say "yep". The intent behind that was a bit of humor/dramatic irony, given the character's affinity for organizing, and a bit more interest than simply rummaging through each room until you have X amount of gold/items without any kind of challenge present. Implementing a challenge to collect items is something I would like to add to the game.

Things I wish I did differently
I want to change how memory is managed. It is consistently managed, where objects are created and destroyed 1:1.  I have all Items and Rooms created and destroyed in main(), which is consistent, but I want to shift memory management of Items to the Rooms in which they live. 

Additionally, my handling of items, and moving Items between the Room's vector and the Player's vector is messy and doesn't scale. I decided to leave it as-is, since it worked and met the requirements, but this is a high priority fix for expansion. I need a different data structure that can allow me to remove specific items, rather than just from the back like with vectors.

I think without the expanded functionality listed below, the game isn't fun at all. Fun wasn't required to do well on the assignment, but I really would like to have something that is a little more coherent as a game, and not just a collection of requirements.

What I'm Happy With

I spent two days planning before beginning coding. That saved me a non-zero amount of headaches, and allowed me to get into really good flows, because of the direction I provided myself. This program has a lot of moving parts, and I'm very happy that the bugs I had were trivial and quickly fixed.

Things I want to add:
  • More tracks, more endings
    • Different win conditions
  • More rooms
  • More movement
    • Ability to move more ways than just "next room"
  • More immersion and interaction
    • "Look around" option, to describe the room you're in
    • "Grab/touch/investigate" option for items
  • More items, more item diversity
  • Challenges to collect items
    • riddles, puzzles, stat checks, etc
  • More story, in general, really
  • Stats, ie Charisma
    • Corresponding stat checks for interactions
  • Remove the time limit/ ruin falling apart
  • ASCII art/small animations
  • Item system that scales
I think my PM background gave me a good sense of what I would be able to do in the time allotted, and I felt well-prepared to slim down and streamline the entire project. I already had most of the above features planned and pseudocoded, but realized I would not have the time to include them. My game functions 100% to spec, is a good jumping off point for a bigger game, and I finished with a few days to spare! 

While the plot is weak and needs expanding, I like the premise, and think it's a lot of fun. I'm a big fan of mellow RPGs like Pokemon and Earthbound, and so tried to pay slight homage to them with the style of flavortext I used. I love the high fantasy genre, and am very inspired by the immersion created by series like the Elder Scrolls games, and the A Song of Ice and Fire novels.

Testing 
I unit-tested as I went. This saved me another huge headache when I did integration testing. My game is essentially just status checks, and memory allocation, so testing each unit was quite simple, but did create lots of small test-cases. As part of the assignment, we were required to turn in a detailed testing document, but here's my boiled-down, tl;dr test-cases:

Rooms and Items:
  • Allocate memory for each room
  • Call all functions of each room
    • Do they function as expected?
    • Does the proper flavortext show?
    • Add print statements to make sure any numbers are properly changed
  • Allocate memory for Items in a room
  • Put Items in the Room's Item vector
  • Do Items not exist any more when they are moved into the players inventory?
  • Are all attributes of the Item correct?
  • Is an error displayed for trying to collect items twice?
  • Are the room Statuses being correctly updated
    • ie, if I turn on the lights in room 1, does the flag variable represent that?
  • Deallocate all Item and Room memory
Player:
  • Allocate memory for player Character
  • Is the location being tracked correctly?
  • Can they interact with all options, rooms and items as expected?
  • Is their inventory print correct?
  • Does the win condition trigger the win events and only the win events?
  • Deallocate memory for player Character

The biggest hurdle during integration was making sure all objects were being properly created, used, and deleted. I was very meticulous, and that paid off. Using Visual Studio for syntax and Valgrind for memory leaks really came in handy.

Final notes:
With some assignments, I found myself needing additional clarification, or became stuck and without direction during the coding or planning processes. I obviously survived (and got an A- in the class to boot), but with this project, I felt very capable. I didn't need help, or feel stuck, and I was able to debug very effectively. The 2 days I spent planning before coding proved to be very helpful. I'm very physical and tactile, so handwriting some pseudocode, class diagrams, and other notes did a lot for my understanding and overall approach. 

I had a ton of fun combining my love of high fantasy with coding. I want to take a whack at another project that combines the two!

You Might Also Like

0 comments