http://www.perlmonks.org?node_id=975997

Dipseydoodle has asked for the wisdom of the Perl Monks concerning the following question:

Hello perlmonks. I apologize for my lack of knowledge here. I am a hobby programmer, not a professional. I have been writing a MOO (multi user dungeon game) and I am having problems figuring out what I shall do with objects within the game. right now I have a single room with objects. The problem is not making it known to the player that the objects are there but simply how to interact with the objects.

#!/usr/local/bin/perl print"\n"; print "#---#--#-####-#----####-#####---#--#---####\n"; print "-#-##-#--####-#----#----#---#--#-##-#--####\n"; print "--#--#---####-####-####-#####-#---#--#-####\n"; print "\n"; print "|The Lounge|\n"; print "You are in a room littered with lines of text emitting from var +ious computer connections from around the world. Above you hangs a dr +oning chandelier which seems to dim and increase lighting every so of +ten. The pleasant sound of a Jazz-Fusion band is heard here.\n"; print "You can see a wooden table here. You can SIT at the table. You +see a 1970's Jukebox here.\n"; print "You can go OUT.\n";

That's what I have... The objects describes as the Table and Jukebox are what I would like to be able to interact with. The question is what kind of syntax should I use?

I would like something like this sit table = print "You pull up a chair and sit at the table/";

Replies are listed 'Best First'.
Re: Perl Moo.
by stevieb (Canon) on Jun 13, 2012 at 15:56 UTC

    Here's a basic example using a dispatch table-driven menu system:

    #!/usr/bin/perl use warnings; use strict; my %dt = ( 'sit down' => sub { print "\nYou pull up a chair to sit you +r lazy butt on\n\n"; }, 'throw rock' => sub { print "\nYou just broke the damned wind +ow!\n\n"; }, 'have drinks' => sub { print "\nWow, you're gonna be hungover +tomorrow!\n\n"; }, 'suicide' => sub { print "\nGame over!\n\n"; exit; }, ); while ( 1 ){ print "\nPlease type an action from this list: "; print join( ', ', keys %dt ); print "\nYour selection: "; chomp( my $action = <STDIN> ); if (! defined $dt{ $action } ){ print "\nThat is an invalid action!\n\n"; next; } $dt{ $action }->(); }
Re: Perl Moo.
by cavac (Parson) on Jun 13, 2012 at 18:00 UTC

    I pulled together a small, working mini adevnture. I decided to split the definition of objects and rooms into two different hashes.

    Objects can also be in a virtual room (like the "inventory" room i use here. But you can also send them into a non-existing room to make them go away or vice versa.

    The anonymous subs also allow you to change objects and rooms as you wish. For example, a closed door might have an action "open" that rewrites it's own description as well as adding an exit to the room. Closing that door again can do the opposite (return original description, remove the rooms exit)

    Here's the code:

    #!/usr/bin/env perl use strict; use warnings; # Current location of player and a helper variable my $location = 'lounge'; my $oldlocation = ''; # define some rooms to have, uh, room to run around my %rooms = ( lounge => { name => 'The Lounge', description => 'You are in the lounge, a rather gloomy place.' +, exits => { 'west' => 'cavern1', 'east' => 'monastery', }, }, 'cavern1' => { name => 'Dark cavern', description => 'You are in a dark cavern. Bloodstains mare the + walls', exits => { east => 'lounge', west => 'cavern2', }, }, 'cavern2' => { name => 'Completly dark cavern', description => 'You are in a very dark cavern. You hear strang +e noises, but the only thing you can see is a glimmer from the easter +n entrance.', exits => { east => 'cavern1', west => sub {die "You get eaten by a grue!";}, north => sub { print "A hyperdimensional field wraps you in color +ed lights.\n"; sleep(10); print "You feel like you are beeing pulled into a +randomly selected room...\n"; sleep(10); if(rand(100) > 50) { $location = 'cavern1'; } else { $location = 'lounge'; } print "You materialize in...\n"; sleep(10); }, south => sub {die "NO-COFFEE-ERROR: Unimplemented room!" +;}, }, }, 'monastery' => { name => 'The Monastery', description => 'You are in the monastery. All around you, g +eeks are writing Perl code.', exits => { west => 'lounge', }, }, ); # Put some stuff in that room my %objects = ( 'table' => { location => 'lounge', short => 'a small wooden table', long => 'It\'s a small, wooden table. It looks cheap.', actions => { take => 'You put the wooden table in your magic pocket. +', drop => 'You carefully put down the table.', }, }, 'monk' => { location => 'monastery', short => 'a Perl monk', long => 'A scruffy guy with long hair and a T-Shirt that re +ads "touch() && die()"\n', actions => { touch => sub{ print "You touch the monk. He instantly send SIGNAL 11 + back to you\n"; die; }, }, }, 'book' => { location => 'inventory', short => 'a pocket book', long => 'It\'s the Perl regular expression reference guide +pocket book.', actions => { take => 'You jump up and down in joy!', drop => 'Your mood suddenly changes for the worse :-(', read => 'You suddenly feel much smarter.', }, }, 'sign' => { location => 'monastery', short => 'a sign', long => 'A big warning sign with big red letters!', actions => { read => 'It says: "TYE BROKE THE MONASTERY!"', }, }, ); while(1) { #print the room description if($location ne $oldlocation) { $oldlocation = $location; print "\n\n"; print $rooms{$location}->{name}, "\n"; print '=' x length($rooms{$location}->{name}), "\n\n"; if(!defined($rooms{$location}->{visited}) || !$rooms{$location +}->{visited}) { $rooms{$location}->{visited} = 1; print $rooms{$location}->{description}, "\n"; } print "You see:\n"; foreach my $key (sort keys %objects) { if($objects{$key}->{location} eq $location) { print " ", $objects{$key}->{short}, "\n"; } } print "There are exits in this direction(s): ", join(',', sort + keys %{$rooms{$location}->{exits}}), "\n"; } # Ask the player what to do print "What next: "; my $cmd = <>; # Primitively clean up the input chomp $cmd; $cmd = lc $cmd; $cmd =~ s/(\s+)/\ /g; $cmd =~ s/^\ //; $cmd =~ s/\ $//; next if($cmd eq ''); # allow the player to finish if($cmd =~ /(quit|exit)/i) { print "Goodbye.\n"; last; } # We don't like nice users. Just state what you want! if($cmd =~ /please/i) { print "I don't know how to please!\n"; next; } # Force re-displaying the room description if($cmd eq 'look') { $oldlocation = ''; $rooms{$location}->{visited} = 0; next; } # Wanna take us to another room? if(defined($rooms{$location}->{exits}->{$cmd})) { # Check to see if its a code section, else just move # the player to that room if(ref($rooms{$location}->{exits}->{$cmd}) eq 'CODE') { $rooms{$location}->{exits}->{$cmd}->(); } else { $location = $rooms{$location}->{exits}->{$cmd}; } next; } # The inventory command if($cmd eq 'inventory') { print "Your open pocket reveals:\n"; foreach my $key (sort keys %objects) { if($objects{$key}->{location} eq 'inventory') { print " ", $objects{$key}->{short}, "\n"; } } } # Look for a simple two-verb command if($cmd =~ /(\w+)\ (\w+)/) { my ($verb, $thing) = ($1, $2); # Check if the object exists if(!defined($objects{$thing})) { print "Sorry, the programmer never invented a $thing!\n"; next; } # Ok, next, let's see if the object is in reach if($objects{$thing}->{location} ne 'inventory' && $objects{$th +ing}->{location} ne $location) { print "You can't see $thing anywhere near you!\n"; next; } # Allow "describe" action if($verb eq "describe") { print $objects{$thing}->{long}, "\n"; next; } # Check if it is an allowed action for this object if(!defined($objects{$thing}->{actions}->{$verb})) { print "You can't do that to $thing\n"; next; } # First of all, if the action is a sub, execute it and be done + with it. if(ref($objects{$thing}->{actions}->{$verb}) eq 'CODE') { $objects{$thing}->{actions}->{$verb}->(); next; } # Inventory-related actions if($verb eq 'take') { if($objects{$thing}->{location} eq 'inventory') { print "You already have that in your magic pocket!\n"; next; } $objects{$thing}->{location} = 'inventory'; print $objects{$thing}->{actions}->{$verb}, "\n"; next; } if($verb eq 'drop') { if($objects{$thing}->{location} ne 'inventory') { print "You can't find that object anywhere within your + vast magic pocket!\n"; next; } $objects{$thing}->{location} = $location; print $objects{$thing}->{actions}->{$verb}, "\n"; next; } # Uhm, ok, this seems to be a text-printing-only action, like +reading a sign print $objects{$thing}->{actions}->{$verb}, "\n"; next; } # Scratch head and try again if(rand(100) > 50) { print "Sorry, could you state that more clearly?\n"; } else { print "I'll just stand around scratching my head instead...\n" +; } }

    Of course, you might want to split all the room and object definitions into multiple files.

    "You have reached the Monastery. All our helpdesk monks are busy at the moment. Please press "1" to instantly donate 10 currency units for a good cause or press "2" to hang up. Or you can dial "12" to get connected directly to second level support."

      That's slighty more complex then I wanted *takes a look back and screeches*

      But it's really funny you mentioned Inform7, it happens to be my favored language for gaming (I have released a few games actually) but I wanted a step up, just not this much.

      Also, Inform7 is awesome, however I need this to be MULTI-USER not single player.

      Time to look for another route I guess, maybe Python or Ruby?

        however I need this to be MULTI-USER not single player.

        Multi-User is always way more complicated, no matter the programming language. You have to share data, allow concurrent access and so on.

        That said, would a web based solution work for you? If so, you could try a combination of Perl/CGI, HTML/Javascript/Ajax and maybe a database/memcached as data backend.

        "You have reached the Monastery. All our helpdesk monks are busy at the moment. Please press "1" to instantly donate 10 currency units for a good cause or press "2" to hang up. Or you can dial "12" to get connected directly to second level support."
Re: Perl Moo.
by Khen1950fx (Canon) on Jun 13, 2012 at 16:59 UTC
    Here's something to play with. I used Moo.
    package Lounge::Table; use strict; use warnings; use Moo; use Sub::Quote; print "\n"; print "|The Lounge|\n"; sub table { my $self = shift; my $table_type = shift || 1; $self->wood( $self->wood - $table_type ); } has bench => ( is => 'ro', ); has type => ( is => 'ro', isa => sub { die "lounge table $!\n" unless $_[0] eq 'lounge_table' }, ); has wood => ( is => 'ro', isa => quote_sub q{ die "$_[0] isn't littered with text from computers!\n" unless "$_[0] is littered with text from computers!\n" }, ); 1; my $sit_down_at = Lounge::Table->new( bench => 'comfortable', type => 'wood', table => 'littered with text from computers', ); $sit_down_at->lounge_table; say $sit_down_at->table;

      Can you explain what Sub::Quote does in Moo? I read the description provided from the Moo doc but I don't understand why inlining is any better than a regular coderef and I'd hate to start using it without knowing why I should be or what it does.

      SUB QUOTE AWARE "quote_sub" in Sub::Quote allows us to create coderefs that are "inlineable," giving us a handy, XS-free speed boost. Any option that is Sub::Quote aware can take advantage of this.

Re: Perl Moo.
by sundialsvc4 (Abbot) on Jun 13, 2012 at 17:28 UTC
    > hello sailor
    Nothing happens here.

    While I am absolutely delighted to see an “interactive fiction/MUD” effort starting here, I would cordially suggest that perhaps you should first review the open-source tools that are already out there which are expressly designed for this purpose.   They already have vocabulary, parsing, data representation, multiplayer and other concerns well in hand... a big leg-up.   The Wikipedia articles on “interactive fiction” and “MUD” (Multi-User Dungeon) are foundation resources.

    > give all spare time to dungeon game
    Taken.

      While you are quite right that using a complete, fully functional game engine is a good idea for most projects, you may be overlooking a few crucial points:

      • Writing a game engine is a very valuable experience.
      • Writing an old school text adventure game from scratch is quite a lot of fun.
      • Many geeks who write games don't actually play them. They just like coding them. (At least thats my personal experience when i write games).

      Btw, another very good adventure game engine/IDE is Inform7. You basically describe the scenes, objects and interactions in more or less plain english and it "compiles" it into a working text adventure. I must admit, it's not completly to my liking (quite challenging to do some standard stuff). Here's an experiment i cobbled together a few months ago:

      Not everything in this (rather badly designed) example works as expected, it's quite a mess and some things i did are needlessly complicated. But hey, i am a Perl guy. Explaining something to a computer in plain english lacks style ;-)

      "You have reached the Monastery. All our helpdesk monks are busy at the moment. Please press "1" to instantly donate 10 currency units for a good cause or press "2" to hang up. Or you can dial "12" to get connected directly to second level support."