#! /usr/bin/perl -w use strict; my $game = "rdfg"; # game name # # rdfg: simple adventure game. # # to win, you have to rescue Prince Danbry from the clutches of # the evil daemon Ice-oh, in the castle of the Rand Witch. # # a node can # * give you something # * take something away # * tell you something # # a node can have links to other nodes; these may be # guarded: if you don't have a key, you get to an access # denied link instead. my %thingtable = ( "01" => "a large purple bath towel", "02" => "a bar of soap", "03" => "a metal collar", "04" => "the sword of Rand", "05" => "a piece of XML", "06" => "Prince Danbri", "39" => "Quest: free Prince Danbri" ); my %nodes = ( "run away" => [ [], { story => "Oh no! You start to run but your feet are still soapy! You trip over and cut your head open. Dismal failure!" } ], "start" => [ [], { give => [ "01", "02" ], "story" => "You reach out of the shower and grab your towel, but as you do so you feel grass under your feet and a cold wind on your back! You turn round and realise that you are standing outside on a grassy hill, with only a damp towl and a bar of soap. There are about a dozen women here wearing black pointy hats and robes, one of whom says, I think it worked!. A nearby road leads towards a distant town.", "links" => { "talk to the witches" => "talk to the witches", "go to the town" => "yellow brick road", "run away! run away!" => "run away" } } ], "talk to the witches" => [ [ "01" ], { story => "The witches tell you that the King is distraught, because his favourite son Prince Danbri has been kidnapped! A prophecy said that none from the entire kingdom could rescue him, so they have summoned you! You must go to the King at once!", links => { "run away!" => "run away", "more nude gossip" => "natter with witches", "go to the Castle" => "yellow brick road" }, give => [ "39" ] } ], "yellow brick road" => [ [ "02" ], { story => "You dropped your soap and slipped! You fell into the river! Well, you recovered and found the soap again, but your towel floated down the river and is lost. You're back on the path, but naked.", take => [ "01" ], links => { "go back to the witches" => "talk to the witches", "try to swim" => "river", "walk some more" => "yellow brick road" } }, [ ], { # no towel story => "The road is crumbly and it's hard not to hurt your bare feet, but after a while it turns to a soft warm mud. To either side there are green rolling hills, although there is a deep and fast river here too. In the distance you can see a small town. As you get closer you see it's not a town but a huge stone Castle, with banners flying from round towers.", links => { "approach the Castle" => "approach town", "go for a swim" => "river" } } ], "approach town" => [ [] => { story => "What at first appeared to be a town is really not one castle but two! One is painted white with flying blue flags, each in the form of three W's. The other is black, and rotting corpses hang in cages from its walls.", links => { "go to the white castle" => "white castle", "go to the black castle" => "black castle" } } ], "white castle" => [ [ "06" ] => { story => "Trumpets sound! You're led before King Eric II! You have rescued the Prince! Eric and the Prince embrace happily, and you are given a great reward: you are made Earl of the Nodes and Arcs!
You have won the game." }, [ "39" ] => { story => "You are granted an audience before King Eric II! A great banquet is prepared, but before you can enter the Hall a messenger arrives and announces that the corpse of Prince Danbri has been lowered in a cage outside the wall of the Black Castle. They say that he was exposed to patent lawyers, an unspeakably horrible death. King Eric no longer needs you, and in his anger and grief has you executed." }, [ "01" ] => { story => "You are granted an audience before King Eric II! But when he sees your towel, he cries out, A usurper! Purple is the colour of Royalty! Your towel is confiscated, and you are marched away and beheaded." }, [ ] => { story => "You are granted an audience before King Eric II! He speaks quickly, My son, Prince Danbri, has been captured and is imprisoned in the Black Castle of Eyebeam! But we of the Free Web cannot enter. You must rescure the Prince! Go back! Return when you have found him!" } ], "execute" => [ [ ] => { story => "You are led away and executed by Sir Eyebeam and the Knights of the Royal Tease! Prince Danbri is made to serve on a RAND specification and the world ends!" } ], "enslave danbri" => [ [ "06" ] => { story => "The guards take your Prince Danbri and enslave him. He is made to serve on a RAND specification! They let you out of the back door, and the witches catch you and eat you." } ], "surrender" => [ [ "02" ] => { # is this reachable? story => "You kneel on the cold hard floor, and kiss the boots of the Wicked Lord of Rand. He strikes you across your back with a whip! You must lie flat on the gound, worm! You have nothing to cross-license! You grovel naked before him, but as you do so you throw the bar of soap at the Lord. When it touches him, he screams! Aaaaagh cleanliness! But the soap falls to the ground, you are whipped and beaten for your insolence, and sold into slavery.", }, [ ] => { story => "You kneel on the cold hard floor, and kiss the boots of the Wicked Lord of Rand. He strikes you across your back with a whip! You must lie flat on the gound, worm! You have nothing to cross-license! You grovel naked before him, and after you have been whipped unconscious you are sold into slavery." } ], "tim speech" => [ [ ] => { story => "There's a puff of smoke and a dramatic roll of drums and Tim appears! He gives an impassioned space about how princes must be free, but he suddenly starts to speed up and speak faster and faster! He spins round and round, and the guards fall down dizzy. As Tim vanishes into the mists of the Frantic When, you see your chance!", links => { "escape to white castle!" => "white castle" } } ], "black castle" => [ [ "01" ] => { story => "The guards at the door see your purple towel. But purple is a colour permitted only to royalty!
You are led away and executed by Sir Eyebeam and the Knights of the Royal Tease." }, [ ] => { story => "You are led into the audience chamber of the Wicked Lord of Rand. He laughs at you, naked and alone. He bids you approach and kneel before him, to kiss his booted feet. The guards look menacing and you approach.", links => { "lick his feet" => "surrender", "attack prince of darkness" => "attack" } } ], "attack" => [ [ "03" ] => { story => "Naked and unarmed, you leap at the Prince of Rand! You reach him, but he raises his sword. At the last moment, you throw the collar at him! It snaps closed around his neck! A strange blue glow surrounds him, and he screams, Nooooo! I've been enslaved. He is teleported to the slave market. You pick up his sword and sit on his throne. It's cold under your naked butt, but you're alive!", take => [ "03" ], give => [ "04" ], links => { "search the Black Castle" => "search black", "go to the White Castle" => "white castle" } }, [ ] => { story => "Naked and unarmed, you leap at the Prince of Rand! You reach him, but he raises his Sword of Royalties. Oh dear. Maybe there was something you could have picked up somewhere along the way that might have helped? This is, sadly, your last thought before Lord Rand teleports you to the Tag Mines, where you live out the rest of your miserable and pathetic life as a corporate slave." } ], "search black" => [ [ "06" ] => { # got danbri story => "You continue down the corridor with the Prince, but suddenly you are surrounded by guards! They say that you must pay a royalty before you can implement Escape!", links => { "surrender" => "execute", "summon Tim" => "tim speech", "give the Prince to the guards" => "enslave danbri" } }, [ "05" ] => { story => "You use the Sword of Rand to cut the leather thongs. Beyond is a small cage, and trapped inside is a Handsome Prince, bound hand and foot with iron chains, and made to listen to endless recordings of patent lawyers reciting legalese. You use a pointy XML bracket to pick the locks that hold him, but the XML is no longer well formed! He is barely conscious, but you can help him out. He mutters that his name is Prince Danbri.", give => [ "06" ], take => [ "05" ], links => { "continue searching the Black Castle" => "search black", "go to the White Castle" => "white castle" } }, [ "04" ] => { story => "You pad softly thorugh the dark castle, your way lit by the dull glow of the Sword of Rand. The floor is cold under your feet, and at times slimy. You feel a chill wind on your back, and discover an iron grate in the wall, held in place by leather thongs. Maybe they are made of human leather. On the floor is a fragment of pointy XML. Ouch, it hurt your foot!", give => [ "05" ], links => { "search some more" => "search black", "leave this cold slimy place" => "white castle" } } ], "river" => [ [ "02" ] => { story => "You get wet. You lose lots of stuff, but you get clean. The river is very rapid here, and you're carried along to some rocky rapids! On no! But worse! There's a waterfall! You fall far and dive into a deep pool.", take => [ "01", "02", "03", "04", "05", "06", "07" ], links => { "walk some more" => "yellow brick road" } }, [ "01" ] => { story => "You get wet. You lose tons of stuff, but you get clean. The river is very rapid here, and you're carried along to some rocky rapids! On no! But worse! There's a waterfall! You fall far and dive into a deep pool.", take => [ "01", "02", "03", "04", "05", "06", "07" ], links => { "walk some more" => "yellow brick road" } }, [ "03" ] => { story => "You swim naked in the cold river, but you can't carry the metal collar, so you wear it on your neck. It doesn't come off and turns out to be too heavy. You drown. Uh oh.", take => [ "03" ] }, [ ] => { story => "You plunge naked into the ice water. You are carried along with the river until you reach a pool where the current is slower. In the mud at the riverbank is a metal collar.", take => [ "01", "02", "04", "05", "06", "07" ], give => [ "03" ], links => { "go back on the road" => "yellow brick road", "wear the collar" => "wear collar" } } ], "wear collar" => [ [ "03" ] => { story => "You stand naked by the river and fasten the collar about your neck. A strange blue glow surrounds you, and you hear a voice, You have chosen the path of slavery. You are teleported to the centre of a market and sold to the highest bidder. But meanwhile, Prince Danbri is tortured to death by the Evil Minions of Rand. You lost.", take => [ "03" ] } ], "natter with witches" => [ [ "02" ], { story => "The witches look at your naked body and titter. Then they see your bar of soap and take it away, muttering about scented baths and candles.", take => [ "02" ], links => { "run away!" => "bad ending 001", "go to the Castle" => "yellow brick road", "more nude gossip" => "natter with witches" } }, [ ], # i.e. nothing carried { story => "The witches look at your naked body and titter. You're covered in dirt! they say. One of them kindly gives you a bar of soap.", give => [ "02" ], links => { "run away!" => "bad ending 001", "go to the Castle" => "yellow brick road", "natter some more" => "talk to the witches" } } ] ); my $ME = $ENV{SCRIPT_NAME} || $0; my $title; my $destination = $ENV{PATH_INFO} || "start"; $destination =~ s/\+/ /g; $destination =~ s/%20/ /g; # other hex escapes left as is to avoid / or ~ or .. $destination =~ s@^/@@; my %things = (); my $items = 0; if ($destination =~ m@^(\d+)/([^/]+)$@) { ($items, $destination) = ($1, $2); } elsif ($destination ne "start") { $title = "bad destination, $destination"; $destination = "start"; } if (!exists $nodes{$destination}) { $title = "unknown destination, $destination"; $destination = "start"; $items = 0; } if (!$title) { $title = $destination; } # now decode the items sub decode_items($) { my ($items) = @_; if ($items == 0) { return [ ]; } my ($checksum, $bits) = ($items =~ m/^(\d\d)(\d+)$/); if (!defined $bits) { return [ "a little girl stole my bits" ]; } # first do a checksum # my $sum = 0; my @result = (); while ($bits =~ m/(\d\d)/g) { if (exists $thingtable{$1}) { $sum += $1; push @result, $1; } else { return [ "$1: the gods can create, but mortals can only destroy" ]; } } if ($sum % 100 != $checksum) { return [ "a naughty boy tried to cheat" ]; } return \@result; } sub encode_items(@) { my (@items) = @_; my $result = ""; my $checksum = 0; foreach (@items) { if (exists ($thingtable{$_})) { $checksum += $_; $result .= "$_"; } } return (sprintf "%02d", $checksum % 100) . $result; } if ($items ne "0") { my $itemref = decode_items($items); foreach (@$itemref) { # print "#### you've got item $_\n"; $things{$_}++; } } sub showpage($); showpage($destination); sub got_everything($) { my ($needed) = @_; my $i; foreach (@$needed) { if (!exists $things{$_}) { return 0; } } return 1; } sub choosenode($); sub choosenode($) { my ($page) = @_; my $list = $nodes{$page}; # $List is a list whose members alternate beteen a list of # required credentials and a hashtable for (my $i = 0; $i < $#$list; $i += 2) { if (got_everything($$list[$i])) { return $$list[$i + 1]; } } return choosenode("start"); # hope this works! } sub lmthprint($) { my ($input) = @_; # convert pointy brackets to html # $input =~ s/[&]/\&/g; $input =~ s/[<]/\</g; $input =~ s/[>]/\>/g; print $input; print "
\n"; } sub showrdf($$) { my ($title, $n) = @_; sub about($$$$) { my ($ifprint, $about, $relation, $resource) = @_; if (!defined $resource) { $resource = "internal_error"; } if ($ifprint) { lmthprint " "; lmthprint " "; lmthprint " "; } $about =~ s/ /+/g; $relation =~ s/ /+/g; $resource =~ s/\s+/ /gms; $resource =~ s/<[^<>]+>//g; if (length($resource) > 25) { $resource = substr($resource, 0, 25); $resource .= ". . ."; } $resource =~ s/ /+/g; return $about . "%09" . $relation . "%09" . $resource . "%0A"; } print ""; my $imgdata = ""; lmthprint ''; $imgdata .= about(1, $title, "story", $n->{story}); $imgdata .= about(1, "player", "is at", $title); my $links = $n->{links}; if (defined $links) { print "
\n"; foreach my $k (keys %$links) { $imgdata .= about(1, $title, "link", $k); } } else { lmthprint("\n"); } my $give = $n->{give}; if (defined $give) { print "
\n"; foreach my $g (@$give) { $imgdata .= about(1, $title, "give", $thingtable{$g}); } } my $take = $n->{take}; if (defined $take) { print "
\n"; foreach my $t (@$take) { $imgdata .= about(1, $title, "take", $thingtable{$t}); } } foreach my $t (keys(%things)) { if (exists $thingtable{$t}) { $imgdata .= about(1, "player", "has", $thingtable{$t}); } else { print "(what's $t?)
\n"; } } lmthprint '
'; print "
"; if ($imgdata ne "") { my $uri = $ME; $uri =~ s@/[^/]*$@@; print ""; # graphViz # print ""; # LiamViz # print "link"; } } sub showpage($) { my ($page) = @_; my $n = choosenode($page); print "Content-type: text/html\n\n"; print "${game}/$page\n"; print "\n"; if (exists $n->{give}) { my $itemlist = $n->{give}; foreach my $item (@$itemlist) { # print "\n###### give me $item\n"; $things{$item}++; } } if (exists $n->{take}) { my $itemlist = $n->{take}; foreach my $item (@$itemlist) { # print "\n###### take away $item ($thingtable{$item})\n"; delete $things{$item}; } } print "

$title

\n"; print "
\n"; print "

", $n->{story}, "

\n"; print "
\n"; print "\n"; print "\n"; print "\n"; print "
you have:"; foreach my $t (keys(%things)) { if (exists $thingtable{$t}) { print $thingtable{$t}, "
\n"; } else { print "(what's $t?)
\n"; } } print "
"; my $h = $n->{links}; foreach (keys %$h) { my $link = $h->{$_}; my $thingies = encode_items(keys %things); $link =~ s/ /\+/g; print "$_
"; } print "start over
\n"; print "

\n"; print "
\n"; showrdf($title, $n); }