[MEGA] The Adventure Continues

Posted by Astryl on Jan. 21, 2012, 12:36 a.m.

Part dev log, part ramble, all Mega.

Because of the length of this blog, and the picky nature of anybody reading it, I've divided it into neat little sections again.

This time I'm dealing with four topics:

> Text Parsing for Adventure Games

> How far my adventure game is coming along

> My impressions of Terraria so far

> Another show I'm watching now which is awesome.

Text Parsing for Adventure Games

I don't know about any of you other programmers out there, but a text adventure game was one of the first things I tried to make the second I 'got' the basics of my language.

Of course, along the way lie many stumbling blocks, and one of these is that mystical 'black box' in a text-adventure that somehow translates your English sentences into actions. Believe me, it took me a while to figure it out. Also keep this in mind:

Quote:
Rule for working with unfamiliar projects/ideas

Don't try to make an elegant approach. Make a messy one, figure out the pitfalls, and refine the design later.

Well, I was sitting around a few days ago trying to figure out in my mind the way that the text parser was going to work. I settled on a large nested if-else block (The brute force approach, but I'm working by 'Make games, not engines').

Then I figured something out halfway: This isn't much different to the way that I, and many other programmers, handle command-line arguments in our programs.

Let me examine a single piece of user data, a single line of input, and then work through the processing steps involved:

> Reading the text (Input)

> Parsing the text (Processing)

> Executing the correct function(s) based on the text (Output)

Any of you who have taken that boring ol' introductory design course will recognize the three sections there. Let's pick them apart.

Note: The code here is C++, but it shouldn't be hard at all to 'translate' it to your favorite language

Reading the text

The first stumbling block, and often the one that beginners will give up on. Sounds easy, right? Read a line of text? Not if you're not told how to. JAVA courses tend to leave out console input for a long time because it requires at least a minor explanation of exception handling in order to implement it, for instance.

But that's only half of the problem.

Most console input functions have one serious problem: They truncate the resulting input at the first whitespace character (That is, a press of the spacebar, or a press of the enter key).

In cases like this, we'd rather read in a whole line of input, up until the user presses Enter.

Of course, not having any offline documentation for MingW and it's libraries, I went ahead and made my own function, called getline(), to read a whole line… only to find out a month later that the standard library has a getline() function that does the exact same thing. Here's a code snippet:

// Returns a string containing a line of input
std::string getString() {
   std::string res;
   std::getline(std::cin, res);

   return res;
}

(std:: identifiers left in to avoid confusion as to where the datatypes and functions come from).

As you can see, getline takes two arguments. The first is the Source, the second is a Reference to the Destination. In this case, I'm using the Stream CIN (Console IN) as the source. You can also use a file input stream (ifstream).

It blocks until the user has finished entering a line of text and places that text into a string, sans the '\n' escape sequence at the end.

Well, now we can get text. Now how do we do anything meaningful with it?

Parsing the text

Python is a wonderful language for text processing. It contains many functions that modify and process strings in useful ways. But C, or C++, do not.

C++ does offer the upper() and lower() functions, in ctypes.h, but there was another function that I needed: Something that could split a string into sub-strings given an arbitrary 'delimiter'. So I made the split() function.

std::vector<std::string> split(std::string str, char delim)
{
    std::vector<std::string> temp_v;
    std::string temp = "";

    for(int i = 0; i < str.size(); i++)
    {
        if(str[i] != delim)temp += str[i];
        else
        {
            if(!temp.empty())temp_v.push_back(temp);
            temp = "";
        }
        if(i == str.size()-1)
        {
            if(!temp.empty())temp_v.push_back(temp);
            temp = "";
        }
    }

    return temp_v;
}

This is a slow, but sure way to split the string up into sub-strings (Stored in the vector container, which allows for easy random access).

My step with this whole process, after getting the input and splitting it, is to copy the split set into another, but leaving out any unnecessary words, such as AN, THE, A, AT.

Usually for this I create a function that returns bool and scans each word for a match with words in a std::list/vector, but for my game I hard coded an if block like this: (I'm using my own upper() and lower() functions, because I know how they work):

vector<string> strip_uwords(vector<string> text_set) {

vector<string> new_set;

for(int i = 0; i < text_set.size(); i++)
{
	if(( mio::same( mio::lower(text_set[i]), "an") ) ||
	   ( mio::same( mio::lower(text_set[i]), "the") ) ||
	   ( mio::same( mio::lower(text_set[i]), "a") ) ) // Etc
	{
	   // Do not perform any operations
	}
	else
	{
	   // Copy to new set
	   new_set.push_back(text_set[i]);
	}
}

return new_set;
}

Simple, not too fast, but effective (Not fast because I'm not overly concerned about speed when it comes to a text adventure game; I want the codebase to be easy to work with though, which becomes more difficult if I spend time creating a more efficient system.)

Executing the correct function(s) based on the text

This step is a bit difficult and simple to explain at the same time. Difficult because I have to explain the system I currently have going.

In my code, there exist some primary objects (classes):

m_room

m_player

m_item

m_npc

The m_room class contains lists of items and npcs.

On the global level, I have these three important variables defined:

m_room* map_frame[100]; // A 10x10 map grid at a time

m_room* room_ptr; // A pointer to the currently active room.

m_player* player_ptr; // A global pointer to the player

With this in mind, observe a snippet from my code that deals with part of the EXAMINE command:

if(same(lower(tx[0]), "examine"))
{
   // Check element count
   if(tx.size() > 1)
   {
	// Special 'inventory' or 'i' or 'items' cases.
        if(same(lower(tx[1]), "inventory") ||
           same(lower(tx[1]), "i") ||
           same(lower(tx[1]), "items") )
	{
            // Call player's inventory list function
            if(player_ptr)
            {
               player_ptr->act_inventory();
               return 0;
            }
	}
        else
        {
            // Passes 'argument' to the current room's examine code, which 
            // checks it's item list for a match, and displays an 
            // "I don't see the <x> here" message if it can't find one.
            
            if(room_ptr)
	    {
  	       room_ptr->act_examine(tx[1]);
	       return 0;
  	    }
                
        }
   }
   else
   {
      // Call room look code since no 'arguments' provided.
      if(room_ptr)
      {
          room_ptr->act_look();
	  return 0;
      }
   }
}

Now, if you take the time to read it carefully, you'll notice that the whole lot is just a bunch of simple if-else blocks. Nothing spectacular.

It's not a very elegant approach, but that's the whole point: It's not meant to be. It's meant to be readable, changeable, and above all: Bug free.

An interesting thing to note is that all the object types have a set of act_*() functions, which are overridden by inherited classes to define

extra behavior (One NPC's act_conversation() will be different from another's). In the spirit of Java, I'm including all new items/npcs/rooms in

their own header/source sets, in sub-folders to keep everything neat.

Hope this provided an interesting read. I had to get it out.

How far my adventure game is coming along

Well between yesterday and now (The 20th, as of this moment), nowhere. Terraria.

That is, I got nowhere with the code, but I managed to soar ahead with the story and

overall idea. I'm not going to spoil any story, but I can mention settings:

A crazy half-French half-British hamlet during the 1500's. Whether this is historically accurate or not, I don't know (nor care); it suits my story though. What is the story about?

Love, Loss, Revenge, and most importantly: ALE.

I'm an amateur writer. I've written countless beginnings (And even some endings). But like all writers, it takes me ages to actually get anywhere without motivation.

Also, I'm way out of my comfort zone with this story. Writing a romantic comedy is not something I'm used to (It has to be comical. Monkey Island changed my view of adventure games forever).

Anyway, time to devote some time to my game before the Terraria shortcut beckons once more.

– UPDATE AFTER THIS BLOG WAS WRITTEN –

I managed to complete the basics of item/environment interaction.

Also, you now get an amusing assortment of satire when you attempt to walk through a non-existant exit.

Oh. And you can sing… Heh.

So far, so good. I'm very pleased with the way this is turning out. Now I just need to figure out the area layout on paper, and map it into the game. Also, I need to create some puzzles. I've still got nearly 20 days for this, so I ought to be able to flesh it out nicely. I'll probably even be nice and release the source code for it when I'm done =P

My impressions of Terraria so far

Well, I connected my PC to the ADSL yesterday (After transporting it to the other house

on the property, the one my parents dwell in). And soon, I had three of my Steam games

downloaded and ready to play.

Except for L4D2. I put that into my gifts inventory, since I can't possibly download it anytime this decade, unless I miraculously receive unlimited internet from some unknown source soon. Which I'm working on.

So, first of all I tried The Polynomial. Took a bit of getting used to (And it took a while to see past all the funky lights), and I discovered a perfectly challenging shooter underneath.

Then… Terraria. I started it up with my two brothers watching me. First impressions: lol Simple graphics, lol Minecraft knockoff, lol crafting bench, lol NPC help/hint/crafting guide.

And then I started noticing the differences. Terraria makes the exploration factor fun. So much, in fact, that it seemed I just blinked and suddenly it was 1AM. I almost laughed myself into a coma when I discovered and crafted the Goldfish Bowl, and figured it's use out, as well as the bucket.

Single Player has me extremely impressed so far; Multiplayer is going to be a veritable time-drainer, so I daren't play it yet. Not until I have lots of free time.

It's a conspiracy. You just let me win the last comp so I'd have a soul-sucking game to keep me distracted and inable to get any other work done. I SEE YOUR PLANS…. *shifty eyes* I won't give in…. just let me finish exploring this little bit of cave quickly…

– UPDATE, NEXT MORNING AT ABOUT 3AM –

It got me again. Dammit. Also, something amusing. I accidentally summoned another Eye of Cthulhu when one was already on-screen. Twice the fun (I won, of course). Oh, and the villagers in my village were all slaughtered by a vicious army of goblins.

That other TV show I'm watching that's awesome

OK. I've only watched ONE episode, and it's also on The Hub.

No, it's not Transformers. I can't stand the new generations.

The Dan vs. series.

Here's the episode I watched yesterday with my brother:

What made this funnier is that my name is Daniel, and I have a black and white cat.

I do not keep a list of things I dislike intensely though. Heh.

Give it a watch if you dare.

Well, that's another 'larger than 10kb' blog successfully written. Not counting the YT embed's data of course.

I'm going to force myself now to work some more on my game, and actually design some of the story areas before Terraria sucks me in again.

Comments

pounce4evur 12 years, 8 months ago

I think writing a romantic story is probably the hardest kind of writing a person can do. But, from what you said, the setting sounds right, and the gameplay sounds like it's going to be fun.

Remind me when I download it to go walk through nonexistent exits

Terraria seems pretty damn fun. I really wanna go get it now that you've fully described it. DON'T LET IT SUCK YOUR SOUL! RESIST! RESIST!!

Oh god, this cartoon seems so fucking demented and I love it. Why had I never heard of this show before? I'll go watch it when I have some time.

JuurianChi 12 years, 8 months ago

Duuude, this sounds interesting.

TehHoosier 12 years, 8 months ago

Is it gonna be like Sam and Max?

Rob 12 years, 8 months ago

Try digging down until you hit the underworld in Terraria, if you haven't already.

…then defeat the boss down there to turn on "hard mode".

And enjoy 400hp enemies instead of regular 40hp zombies. Ones that take out like 50+ hp off someone with 30+ def, when zombies did 1 damage. I hope you have lots of potions.

Astryl 12 years, 8 months ago

Quote:
Remind me when I download it to go walk through nonexistent exits
With pleasure. :P

Quote:

Terraria seems pretty damn fun. I really wanna go get it now that you've fully described it. DON'T LET IT SUCK YOUR SOUL! RESIST! RESIST!!
I tried. I failed… at least I failed to resist after I got some work done.

@Rob: I've gotten down to the Underworld a few times. Got me a fancy Blue Phase Saber, 25 Defense, and a shitload of Potions (Both Mana and Health). I'm waiting 'til I can make some Hellstone weapons/armor before I tackle the boss down there though…

Rob 12 years, 8 months ago

It's not the boss that's hard. It's the hell that gets unleashed after you defeat it.

Astryl 12 years, 8 months ago

Quote:
It's not the boss that's hard. It's the hell that gets unleashed after you defeat it.
All the more reason to get myself some high-spec armor. And maybe I should buff up my town's defenses a bit. I get tired of waiting for my Merchant to return after he's been sliced down by Zombies.

Rob 12 years, 8 months ago

But you're not going to get the high spec armor before you do that. The higher spec armor is mostly only available after you unleash hell. Well. I guess you could go up to Molten Armor if that's not what you already have. (Hellstone armor) So why aren't you mining that? It doesn't take long to do. Well. It takes a lot longer as of the recent patch (1.1 or whatever) since hellstone blocks release lava. They didn't used to and it was way easier back then.

Astryl 12 years, 8 months ago

I've got a few Obsidian Skin potions for that, but I kept getting flanked by a bunch of Imps. Also, the journey down is taking a while. I'm methodically working on blowing a hole at least halfway down. Good thing I have lots of gold.

Rob 12 years, 8 months ago

Quote:
I'm methodically working on blowing a hole at least halfway down. Good thing I have lots of gold.

WHY

The best way is to just dig a straight hole down with picks. It works REALLY well with two people since you can each do either the left or right block and just both hold with LMB until you reach the bottom.