Advertisement Jump to content
Sign in to follow this  
Kjansen92

Inventory System for Noom Text Adventure Game

This topic is 1887 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all!

 

I'm building a text-based adventure game with only a few rooms. Depending on which (out of 2) room you make an 'event' in, the second 'event' will take place in the other room. There are 72 possible outcomes in the first room, on the first event, and 9 possible outcomes in the second room, on the first event. Depending on which action you take, the second action, which happens in the second room, will be unique.

 

So, there are about 567 unique second actions. Don't worry, I love working this and trying to find a solution.

 

My question is, based on unique results, players will gain some kind of bonus, item, and/or event. I want to be able to add items and stats, etc., to the Player class that I have, but I'm unsure how I should structure that out. There aren't very many character attributes, and there is a reasonable amount of items.

 

The complex part would be taking all of that relevant data and using it to construct the third event in the game, which happens in the third room, regardless of player responses.

 

If you would like me to make this a whole lot clearer and simpler, I would be glad to.

 

Thank you very much for your time,

Kevin Jansen

Share this post


Link to post
Share on other sites
Advertisement

Since inventories usually allow the player to add/remove items at random I suggest you use a linked list. It's a datastructure in which each node contains a pointer to the next node. This means you can insert and remove at your own volition - it's simply a matter of adjusting the pointers (don't forget to free your memory!).

A double linked list means each node contains two pointers (one that points to the next node and one that points to the previous node).

Relevant links: explanation code

 

Stats seems like it would be a datamember of the player instance (that is Player::m_power accessed via Player::getPower(); , Player::m_hp accessed via Player::getHitPoints(), etcetera).

 

Assuming I'm interpreting your question correctly, you could create an Event class and store the events in a std::vector<Event*>. The events would be treated as a subroutine and stored in the Game instance. 

 

Something along the lines of:

GoblinDuel::RunEvent(Player* player) //"GoblinDuel" is a class which inherited from "Event"
{

    bool finishedEvent = false;


    printf("--YOU ENCOUNTER A GOBLIN--");
    printf("PREPARE TO FIGHT!");

    if(player->getHP() < 5)
        {
            printf("Goblin throws you a health potion");
            player->addItem(new HealthPotion()); //add item to linked list
        }

    Goblin* goblin = new Goblin(1000); //goblin with 1000 HP 
    while(!finishedEvent)
    {
        

        //fighting code here


        if(goblin->getHP() <= 0)
        {
            printf("Goblin groans and slumps! You take his sword")
            player->addItem(new GoblinSword()); //add item to linked list
            finishedEvent = true;
        }

         if(player->getHP() <= 0)
         {
             printf("Your guts fall on the floor. The goblin sheaths his sword and walks away");
            finishedEvent = true;

            //don't forget to end the game when we return to main loop
        }
    }
}


Game::RunAllEvents()
{
    std::vector<Event*>::iterator it = m_events.begin();

    while(it != m_events.end())
    {

        it->RunEvent(m_player);
        if(m_player->getHP() <= 0) //player died in event
        {
           this->showDeathScreen(); //you would actually just break and call showDeathscreen() in the main loop but I'm not going to type the whole game =)
           break;
        }

        ++it;
    }
} 

I typed this rather quickly so don't take it as gospel, it's just something to help you get started. Also: events will have to be inserted into the vector in order in which they take place.

 

 

If the events aren't run sequentially you can use an std::map<string, Event*> instead. You can then call the event by name when you need it. That would look something like this

 

Game::runGameLoop()
{
    //add event durin initialization
    m_events.insert(std::pair<std::string, Event*>("goblinDuel", new GolbinDuel()));





    //lots of code goes here




    //somewhere in your game loop
    if(eligibleForGoblinDuel()) //check if we need to run goblin duel
        m_events.find("goblinDuel")->runEvent(m_player);
}


bool Game::eligibleForGoblinDuel()
{
    bool playerHasSword = m_player->hasSword(); //check inventory for sword
    bool isInSecondRoom = m_currentRoom == 2;
    bool playingForTenMinutes = m_timer.getTimeInMinutes() >= 10;
    if(playerHasSword && isInSecondRoom && playingForTenMinutes)
        return true;
    else
        return false;
}
Edited by molehill mountaineer

Share this post


Link to post
Share on other sites

Sounds like most of all you're trying to avoid an insane mess of flags and nested if-blocks.

 

If your items are only strings and you expect to get a ton of them, you can use a std::set, but I doubt you'll come up with enough items to the point where a straight forward std::vector<string> isn't good enough.

 

Your events sound a lot like growing tables and I'd probably try to model them just as such. Things will be relatively flexible if you go look into std::bind (or boost::bind if you're stuck with the old standard).

 

It would all boil down to create conditions, a result and going through that table. For simplicity, let's assume all condition functions take a Player*.

 

bool hasDefeatedDragon(Player* p) { return p->dragonSlayerFlag; }
bool hasItem(Player* p, const string& item) { return find(p->inventory.begin(), p->inventory.end(), item) != p->inventory.end(); }
bool healthIsBelow(Player* p, int threshold) { return p->health < threshold; }
 
typedef std::function<bool, Player*> ConditionFunc;
 
struct EventTableEntry
{
   EventTableEntry(ConditionFunc f1, ConditionFunc f2, ConditionFunc f3, Event event);
 
  //You can use a list or template the number of preconditions if needed or use a dummy condition "ignore" that always returns true as filler
  ConditionFunc conditions[3];
  Event event;   
}
 
//Now why use std::bind? Because for this to work, all functions need to be called in the same way. Bind allows you to set certain parameters at construction time of the function object, so you won't have to pass it as a parameter when you actually call it.
 
EventTableEntry eventTable[] =
{
   EventTableEntry(isHealthy, std::bind(hasItem, _1, "sword"), ignore, spawnSkeleton);
   EventTableEntry(...);
};
 
Event* getAppropriateEvent()
{
   for(auto e : eventTable)
   {
        if (e.condition[0](player) && e.condition[1](player) && e.condition[2](player)) return &e.event;
   }
   return nullptr;
}

 

It's all crude pseudo code and taking some shortcuts, but might demonstrate the idea as such.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!