Procedural Quest Generation in Perl

Yes, it's another post about Veure (whose actual name we might finally have chosen, but that's another story), the MMORPG that I've been writing.

There are 117 stars in a 20 light year radius around Sol. There are 544 space stations and currently there are 3,080 rooms in those stations (and that number is growing). That means there's a lot of area, but how do I fill that area? There's a lot of work still left to be done, but I took a quick stab at implementing a procedural mission generator as described in this paper. Surprisingly, the core of the code only took about an hour to write.

The missions won't be generated on the fly, but I'll still need to write many, many missions for Veure. While some of the missions will be carefully tailored for specific game play reasons, there's nothing wrong with auto-generated missions if done correctly (and manually vetted!). For example, if you're at the port of The House of Wolcott station in the BD+20 2465 star system and you've saved enough money to buy a ship, maybe you have a hold full of Tritium and you're flying to Gerry Stronghold in the Altair system because currently Tritium is paying a premium there. If you happen upon a mission asking you to get a Lassiter, goto Altair and find Durran Haymer and deliver it to him (for legal reasons, I'll have to change the names), do you really care if it's auto-generated? For many missions, you won't.

That example, by the way, is something I sort of made up, but my procedural quest generator in Perl generated the shell of that quest (I had to fill out the details). It looked like this:

Generating a quest for comfort: Obtain luxuries

['get','goto','>give']
get
    [Go someplace and pick something up that's lying around there] ['goto','>gather']
    goto
        [Just wander around and look] ['>explore']
        >explore
    >gather
goto
    [Find out where to go and go there] ['learn','>goto']
    learn
        [Go someplace, get something, and read what is written on it] ['goto','get','>read']
        goto
            [Just wander around and look] ['>explore']
            >explore
        get
            [Steal it from somebody] ['steal']
            steal
                [Go someplace, sneak up on somebody, and take something] ['goto','>stealth','>take']
                goto
                    [Just wander around and look] ['>explore']
                    >explore
                >stealth
                >take
        >read
    >goto
>give

Curiously, while the original code was written in Prolog, it seemed trivial to convert this to Perl. The core of the above looks like this:

my %generated_subs;
my $current_depth = 0;

foreach my $action ( keys %actions ) {
    $generated_subs{$action} = sub {
        my ( $description, @choice ) = randomly_choose( $actions{$action} );
        $current_depth++;
        my $padding = '    ' x $current_depth;
        if ( $current_depth + 1 > $DEPTH ) {
            @choice = grep {/^>/} @choice;
        }
        say "$padding\[$description] " . Dumper( \@choice );

        foreach my $step (@choice) {
            say "$padding$step";
            if ( $step =~ /^\w/ ) {
                $generated_subs{$step}->();
            }
        }
        $current_depth--;
    };
}

It was carefully chosen data structures which made the above fairly simple to write. Of course, the ability to dynamically create a dispatch table helped.

With a little bit of work, I can replace NPC names, item names and place names with names from the Veure database. To give you an idea of how a quest looks in the original system, here's an auto-generated quest they produced:

  • Steve: Sodan the Destroyer has been captured by some orcs! Steve: We need to rescue him.
  • Steve: You'll need to kill an orc, and I don't where to find it. Return to me when you have succeeded.
  • The player needs to consult Gnasher Furgutt in the Qeynos Hills to learn the location of an orc.
  • The player moves to Gnasher Furgutt in Qeynos Hills.
  • The player asks Gnasher Furgutt about the location of an orc.
  • The player moves to an orc in Blackburrow.
  • The player kills orc.
  • escort Sodan the Destroyer to Qeynos.
  • The player moves to Steve in Qeynos.
  • The player reports back to Steve about the rescue of Sodan the Destroyer

As you can see, with a bit of polish, you can make procedurally generated quests seem interesting. Or you could use the quest generator I wrote to produce the shell of the quest for you and use that as inspiration to create you own quests.

You can check out the full code here.

I plan to do a lot more with this later, but much of it will be proprietary, internal stuff for Veure, but I thought I'd share the core idea with you.

About Ovid

user-pic Freelance Perl/Testing/Agile consultant and trainer. See http://www.allaroundtheworld.fr/ for our services. If you have a problem with Perl, we will solve it for you. And don't forget to buy my book! http://www.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840/