Project: Adventure Game

Create a text-based adventure game that lets the player explore a world and find success or glorious failure!


Overview


Working solo or in a pair, create a C++ text adventure game.

The game flow plays by the player entering a "room" (or area) and then doing some interaction. Some rooms will have some form of loot (an enhancement) that the player can somehow earn or collect which helps the player. Some rooms will lead the player through a challenge that uses the enhancements they have been gathering.

For example, your rooms might have the following types:

  • Find a locked chest which user repeatedly tries to open. When opened gives a sword enhancement.
  • Find a creature who is telling a joke. If insulted it will fight the player.
  • Encounter a creature that attacks the player. If the creature is defeated then it gives you a sword enhancement, but if the player is defeated then the player loses the game.

You must come up with a theme for your game such as zombie apocalypse, cute dragon collector, doctor at a hospital, adventurer in mythic forest, terraforming the moon, defeating midterm exams, researching a virus, solving world hunger, ....


Specific Requirements


  • Room Creation
    • Define a C++ struct type to store information about a room.
      • What you put in the struct depends on the needs of your game.
      • For example, your struct might includes fields for a room name, description, name of a creature or character you might find in the room, reward for completing the room, state of the room (done or not), health of the creature to fight, attack abilities of the creature, ....
      • Some rooms may not need all fields.
      • Expect to add / revise these fields as you develop your game.
    • Create a C++ vector (or array/map/hash-table/dictionary or similar structure) which stores the collection of rooms, initialized to hold the state of the wold.
      • See implementation details below for more on this.
      • In this doc, when it mentions a vector, your alternative data structure is fine.
    • Your game must run entirely based on the rooms you define in your vector of room structs. Your functions and logic must not be hard-coded to the specifics of any room.
      • Solutions that hard-code the rooms or hard-code the map layout in logic will not earn many marks.
      • A good design is to have one function for each type of room. The details of the rooms are stored in the vector of rooms, not hard-coded into the C++ code.
  • Room Types
    • Must have at least 3 room types where the user can have some interaction (such as opening a chest, telling a joke, fighting a character; be creative!).
    • You might also want a room type where there is no interaction (just like walking a path).
    • You might want to make a type of room for the "goal room" which is where the user wins the game.
    • You must have at least two rooms of each type of room you support that has a player action. For example, if your game includes a combat room, you must have two combat rooms; if you support a chest room, you must have two chest rooms. Rooms without actions (like just a path, or possibly a goal state) may appear once.
    • Your rooms can require some player skill (such as solving math or spelling problems), or be much more random choices (like "do you attack or run away?").
  • Room Navigation
    • Your rooms must be connected to each other, and the user must be able to select their path through connected rooms.
    • Rooms must be connected with some branching options (go north or west or south, for example).
    • Suggestion: Add a row and col field to your room struct type and define each room with a location. Then your program can dynamically process your vector of rooms to find where you can go next based on the layout of the grid. For example, perhaps you can go UP if there is a room at the same column as me, but row is one less.
    • Suggestion: You may choose either a static or dynamic map:
      • A static map is one that you craft once and hard-coded into your vector of structs for the rooms. Each time the user plays the game it is largely the same. You may still add randomness to a static map if you like, such as randomizing the enhancements a player collects, or which creatures they encounter within a fixed set of rooms.
      • A dynamic map is one that is created when the program runs. It is not hard-coded into a vector of structs, but rather your program includes an algorithm to generate the vector of structs. The may may be created at program startup or as the player plays the game (if responding to certain player events). The program comes up with a brand new map each time the game is played so that no two plays of the game are the same. If using this approach, your algorithm which generates the map must ensure that all game requirements are met each time the game is played including the number of rooms of each type, the game being playable, and the map being not just a straight line of rooms.
      • These is flexibility in how your program generates the map. You may read in some or all map details from a file if you like, but you don't have to. For example, randomly choose a joke from a text file, or read in the full map from a specific file format.
      • If you are unsure how you want to generate the map, it is easiest to create a static map by hand.
  • Robust Input
    • The user must be able to navigate between rooms using single character inputs such as 'u' for "Up", or 'n' for North (and pressing ENTER).
    • User input must be case insensitive.
    • If user enters an invalid value, they must be prompted to select again.
    • Hint: You'll have a number of menus, so make a function to handle it!
      • For example, pass into your function a vector of strings which are the options the user can select between.
      • Print the vector of strings for the user to see the options they can enter.
      • Have the user enter the first character of one of the strings to select that option.
      • Handle invalid user selections by looping.
      • Return the character the user entered.
      • For example, you might call with:
        char option = askUserMenuChoice({"North", "East", "South", "West"});
        which might show the user:
        What do you want to do?
        (N)orth, (E)ast, (S)outh, (W)est
        >
  • Gather enhancements
    • As the player adventures they should (at least sometimes) be able to collect enhancements.
    • Enhancements make them more powerful in some way, such as weapon enhancements, strength enhancements, or score multipliers.
    • Each time the user enters a room, they should be told their current state for these enhancements, such as:
      In your hand you hold your Glowing Happy Funny sword (25 damage).
  • In-Game Challenges
    • Note: This is not optional; it's an in game requirement to add "challenges".
    • Must have some type of challenge that the player must overcome.
    • The enhancements they have gathered throughout their game must help them in this, such as weapon buffs, more powerful spells, higher charisma, better compassion, increased strength, bigger collection of cute cats, ...
    • The challenge must happen over multiple rounds, such as attacking a monster or trying to be the first to win 10 rounds in a dance competition.
    • The challenge must have an element of randomness, such as adding between 0-10 extra damage.
    • User must be able to choose to continue the challenge or abandon it, such as to attack or run away.
    • If a user abandons a challenge, the user must be pushed back to their previous room.
    • If a user fails a challenge there must be some consequence, such as the game ending or losing an enhancement.
    • If the user succeeds in a challenge, they must receive a enhancement.
  • Reach a goal room and end game
    • One or more rooms in your game must be a goal. When the user enters that room, possibly having completed some task or earned enough enhancements, then they win!
    • User must not be able to simply navigate to the goal room and win immediately without completing any challenges.
    • There is considerable flexibility about how they they win with the goal room.
  • Room being done
    • When a user completes a room that has an action (like defeating an enemy, opening a chest, collecting an additional cute friend, hearing the creature's joke, saving a creature from death, ...) that room must be marked as complete so the user cannot do the action in that room a second time.


Implementation Requirements


  • Constants
    • You must define at least one constant (all upper case). All magic numbers in C++ code should be constants. It is fine to have magic numbers in the code that creates the rooms, such as setting the cuteness factor of a puppy to be 110%.
    • Global constants are OK. Global variables are not allowed.
  • Creating the Game Data

    • Make a function that creates the vector of rooms. For example,
      vector<Room> makeRooms() {
          return {
              {
                  .name = "The bog",
                  .description = "a foul smelling marsh with lush grass.",
                  .type = ROOM_SEARCH,
                  // ...
              },
              {
                  .name = "Tree fort",
                  .description = "an old tree fort is built high in the trees.",
                  .type = ROOM_PAINT,
                  // ...
              }
          };
      }
    • In main(), create a local variable that stores the vector of rooms:
      vector<Room> rooms = makeRooms();
    • In main(), create a data structure to hold the enhancements the player has collected.
      • It will likely be a vector of some enhancement structs that you define.
    • Suggestion: In main(), track which room the user is currently in using the room's index in the rooms vector. This way you can record the player walked to a new room by changing this value.
      int currentRoomNum = 0;
      • Or, you could store their row and column.
      • Don't track the user's movements using a reference to a room because you cannot make it reference a different room after you have created it. Therefore when the user steps into a new room, you cannot change which room that variable references:
        Room &badCurrentRoomIdea = rooms.at(0); // DO NOT DO THIS
    • When you call your functions, pass in a reference to the current room and a reference to the current enhancements as needed. For example:

      void doRoomFightZombieHorde(Room &room, vector<Enhancement> &enhancements) 
      {
          // Describe room
          cout << "You go to " << room.name << endl;
          // Do fight
          ...
          // Get some reward, likely a field of this room
          enhancements.push_back(room....);   
      }
      int main() 
      {
          int currentRoomNum = 0;
          vector<Room> rooms = makeRooms();
          vector<Enhancement> enhancements;
      
          while (...) {
              // Make reference to the current room:
              Room &currentRoom = rooms.at(currentRoomNum);
      
              // Do the fight-zombie-horde room:
              if (/*is currentRoom a zombie horde room*/) {
                  doRoomFightZombieHorde(currentRoom, enhancements);
              }
              ...
              // Move to new room
              currentRoomNum = changeToNewRoom(...)
          }
      }
    • Since nothing is a global, you may need to pass the room and enhancements around to multiple functions. You may sometimes also want to pass all the rooms (for example for navigating between them).
    • Since the state of a room may change during the adventure, you may want to pass-by-reference, not const-ref.


Getting Started


Here are some suggestions on getting started:

  • Decide if you will work with a partner or not.
  • Start by understanding what you need to do.
    • Read this document (done? Good work!)
    • Have a look at the sample output to inspire you.
  • Come up with a theme you love!
    • Plan to have some fun with this; be creative!
  • Design the broad elements of you game including
    • What are the 3 (or more) room types?
    • What is the challenge (like combat)?
    • What are the enhancements the user collects that help with the challenge?
  • Design the structure of your C++ program.
    • What are some of the big functions you'll need. What arguments do they need? What is their return type?
    • How are you tracking the enhancements and the rooms?
    • How are you storing the state for the entire board? Suggested vector of room structs.
  • If working with a partner, decide who will do what.
    • Will you work together pair-programming (both at the room contributing at once, one keyboard).
    • Will you divide the work in half? Who does what? Allow half of the project time for integration and debugging.
  • Write a little code, test a little code.
    • Start with one simple type of room and get something very basic working.
    • Make the overall map data structure create a couple connected rooms.
    • Implement robust keyboard input.
    • Implement the navigation between rooms.
    • Flush out the implementation for rooms.
    • Implement the in-game challenge.
    • Implement the enhancements feature.
  • Play test the whole game
    • Look for things that don't work like doing a room twice, or text that doesn't fit.
  • Double check your solution meets:
    • At least three types of rooms with actions.
    • At least two instances of each type of room with actions.
    • Cannot win game without completing a challenge.
    • C++ logic (loops, if, ...) not hard-coded to a specific instance of a room: all room specific info coming from the room struct.
    • At least one named constant.