+++ /dev/null
-#!/usr/bin/perl
-#
-# This file is part of Grue Hunter.
-#
-# Copyright (C) 2013 Jason Self <j@jxself.org>
-#
-# Grue Hunter gives you software freedom; you can copy, modify, convey,
-# and/or redistribute it under the terms of the GNU Affero General Public
-# License as published by the Free Software Foundation; either version 3 of
-# the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
-# for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program in a file called 'AGPLv3.txt'. If not, see
-# http://www.gnu.org/licenses/agpl-3.0-standalone.html or write to the: Free
-# Software Foundation, Inc., 51 Franklin St, Fifth Floor Boston, MA
-# 02110-1301, USA.
-
-use strict;
-use warnings;
-
-my $instructions = <<INSTRUCTIONS;
-\033[2J
-\033[0;0H
-Grue Hunter version 1.0
-Homepage: http://jxself.org/grue-hunter.shtml
-Copyright (C) 2013 Jason Self <j\@jxself.org>
-
-Grue Hunter gives you software freedom; you can copy, modify, convey,
-and/or redistribute it under the terms of the GNU Affero General Public
-License as published by the Free Software Foundation; either version 3 of
-the License, or (at your option) any later version.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program in a file called 'AGPLv3.txt'. If not, see
-http://www.gnu.org/licenses/
-%
-\033[2J
-\033[0;0H
-In this game you are a brave hunter. You are going to enter an underground
-cave system in search of the Grue. If you can capture the Grue and get out
-alive you will win the game.
-%
-Once in the cave system you can move around or shoot an arrow using the
-commands 'move' and 'shoot' and any of the four compass directions. You can
-also shorten the commands to 'm' and 's' and the directions to 'n', 's', 'e',
-and 'w' for brevity. You may enter 'quit' or 'q' to end the game at any time.
-%
-But... There are other things in this cave system. Giant bats can pick you up
-and drop you elsewhere. There are also bottomless pits. If you fall into one
-of these you will never get out. There is also the Grue itself. Though not an
-aggressive creature, it will eat you if you come too close. There are also
-earthquakes that move things around (the bats, the bottomless pits, the Grue,
-and the exit.)
-INSTRUCTIONS
-
-my $space = <<SPACE;
-The room here slopes downward. Old candles are scattered across the floor.
-An old fountain sits in the middle of the room. It is covered in rubble and dirt. A toppled statue lies to the right.
-The walls here are smooth. There is an old anvil here.
-The walls contain claw marks and some sort of graffiti in a language you cannot read. A crack in the ceiling above the middle of the north wall allows a trickle of water to flow down to the floor. The water pools near the base of the wall, and a rivulet runs along the wall and out into the hall. The water smells fresh.
-You push a heavy stone door open, revealing a room with pillars lining the wall. Several square holes have been cut into the walls.
-Stalactites drip down from the ceiling in sharp points while flowstone makes strange shapes on the floor. Rusting spikes line the walls.
-This room smells of rotting vegetation. Burning torches in iron sconces line the walls.
-The scent of smoke fills the room. A small hole to your left attracts your attention; but it is too small to be of any concern.
-A stream of oil flows from one side of the room to the other, in a small rut in the floor. There is a mysterious stain on the wall.
-This is the center of a narrow passage that connects other rooms. This room is about average size; but is filled with a pungent aroma that is very nauseating.
-There are cracks in the floor here. It is foggy, making it difficult to see.
-Thick cobwebs fill the corners of this room and wisps of webbing hang from the ceiling, wavering in a wind you can't feel. You notice a light humming sound.
-The floor is damp and looks quite slippery. You will need to be very careful here. A haze of smoke hangs in the air.
-This is a very large room with a large boulder in the center of it. Part of the wall has collapsed into this room.
-Several pieces of rotten fruit are scatted throughout the area. A hole has been dug into the wall. After looking inside you see that it doesn’t go anywhere.
-This room is very dusty, and you see footprints set in the dust. There are spiders on the walls.
-The passageway is very low; but if we crawl we can make it. After coming through the other side you see a stone stairway that goes up to the wall, where it abruptly ends.
-As you enter the room part of the ceiling collapses but you jump out of the way in time to avoid getting hurt. Duck your head in here; as large rock stalactites hang from the ceiling.
-This area is covered in rubble and dirt. There is a puddle and scraps of wood here.
-The floor is covered with sand and pottery shards.
-The walls have been engraved with geometric patterns. Fog gathers and swirls on the floor, concealing it.
-There is a salty, wet smell to this area. A small hole in the ceiling lets light from outside through ... But you would never fit through it.
-There are bones and several piece of rotting wood scattered here.
-It is misty here. Several empty flasks are scattered throughout the area.
-Rats run past you, in the direction you just came from. The floor here is covered in perfect hexagonal tiles.
-There are strange looking yellow stones here. Water drips through the ceiling.
-Your nostrils are overwhelmed by a sulphurous smell. The ceiling here is damp.
-Grime and filth smear this room. This area is filled with stalactites and stalagmites.
-This is a very dirty room; it was partially filled in by the last earthquake that hit. A slight breeze blows through this area.
-A shallow pit sits in the center of the room. The walls here have been engraved with spiral patterns.
-This cavern has a high, domed ceiling. There is a mold growing here.
-A slight, damp breeze blows through this area. This rooms smells of mildew.
-This area is very dusty. A dry well sits in the center of the room.
-As you enter this room some small rocks fall from the ceiling. Several broken arrows are scatted about.
-You are in a small passageway. The walls, ceiling and floor are composed of a smooth black stone. Illuminating your way is a silvery white glow coming from two long grooves midway up each wall that run the length of the passageway.
-You hear an eerie sound. Jagged steel blades project from cracks in the walls.
-Someone has scrawled the drawing of a door on the wall. A haze of dust hangs in the air.
-The walls are covered in slime. There is broken glass on the ground.
-There is a green slime on the floor. This room is very small; but I think we can make it through ok.
-There is a stale smell here. Your steps echo here.
-This large room is illuminated by a low-burning fire pit. There is an iron sconce on the wall, but the torch has been removed.
-A rivulet of water slowly trickles from a hole in the wall. A narrow pit covered by iron bars lies in the center of the room.
-Several empty bottles lie on the floor. Water drips from the ceiling, landing on your head.
-You are in a low depression in the center of a medium-sized room. A flurry of bats suddenly fly past you as you enter. Their screeching fills your ears.
-Someone has left a lighted torch on the wall and it illuminates your passage. A liquid-filled pit sits in the center of the room and is so murky that you can't see its bottom. The room smells sour.
-This area is only a few feet high, but crawling on your stomach you make it through. You notice centipedes crawling along the wall.
-A rattling noise abruptly stops when you enter. Black mold grows on the walls and parts of the floor.
-You are in a small room with rocks and debris scattered everywhere. Part of the here ceiling has collapsed.
-A huge iron cage lies on its side in this room, and its gate rests open on the floor. A broken chain lies on the floor. This room has sour odor, like something rotten or fermented.
-An iron chandelier hangs from the ceiling. A old wooden barrel lies on its side, mostly collapsed.
-SPACE
-
-# Constants
-my $NORTH = 1;
-my $SOUTH = 2;
-my $EAST = 3;
-my $WEST = 4;
-
-# Update this constant to increase the number of rooms in the game.
-my $MAX_ROOMS = 50;
-
-# Update this constant if more room descriptions are added.
-my $MAX_ROOM_DESCRIPTIONS = 50;
-
-my $MAX_LINK_COUNT = 4;
-my $EARTHQUAKE = 3;
-my $EARTHQUAKE_CHANCE = 14;
-my $EVEN_CHANCE = 2;
-my $PIT_CHANCE = 32;
-
-my $SET = 1;
-my $RESET = 0;
-
-my $CLOSED = -1;
-my $GRUE = -2;
-my $EXIT = -3;
-my $BAT1 = -4;
-my $BAT2 = -5;
-my $PIT1 = -6;
-my $PIT2 = -7;
-
-# Game State
-my $grue_location = $CLOSED;
-my $exit_location = $CLOSED;
-my $bat1_location = $CLOSED;
-my $bat2_location = $CLOSED;
-my $pit1_location = $CLOSED;
-my $pit2_location = $CLOSED;
-my $player_location = $CLOSED;
-my $grue_bagged = 0;
-
-# Game Space
-my @rooms = ();
-
-sub randomize_rooms {
- my @room_descriptions = split(/\n/, $space);
- my %random_hash = ();
- my $random_room = 0;
- my $random_room_description = 0;
-
- for (my $i = 0; $i < $MAX_ROOMS;) {
- $random_room = int(rand($MAX_ROOMS));
- $random_room_description = int(rand($MAX_ROOM_DESCRIPTIONS));
-
- if (!$random_hash{$random_room}) {
- $random_hash{$random_room} = 1;
- my %room = ();
- $room{'description'} = $room_descriptions[$random_room_description];
- $room{$NORTH} = $CLOSED;
- $room{$SOUTH} = $CLOSED;
- $room{$EAST} = $CLOSED;
- $room{$WEST} = $CLOSED;
- $room{'link_count'} = 0;
- $rooms[$i] = \%room;
- $i++;
- }
- }
-}
-
-sub connect_rooms {
- my $current_link_count = 0;
- my $random_link_count = 0;
- my $rooms_left_to_link = 0;
- my $room_direction_state = 0;
- my $next_room = 0;
-
- for (my $i = 0; $i < $MAX_ROOMS;) {
-
- # Take into consideration current link count and get random link count.
- $current_link_count = $rooms[$i] -> {'link_count'};
- if ($current_link_count < $MAX_LINK_COUNT) {
- $random_link_count = int(rand($MAX_LINK_COUNT - $current_link_count)) + 1;
-
- # Check that there are enough rooms left to link. If there are not
- # then substitute the random link count with the rooms left to link.
- $rooms_left_to_link = $MAX_ROOMS - $i;
- if ($random_link_count >= $rooms_left_to_link) {
- $random_link_count = $rooms_left_to_link;
- }
- }
-
- # If the room is fully linked then set the random link count to 0.
- else {
- $random_link_count = 0;
- }
-
- # For each room create links upto a random link count by going through
- # the vacant compass directions.
- for (my $j = 0; $j < $random_link_count; $j++) {
- for (my $k = 1; $k < 5; $k++) {
- if ($i < $MAX_ROOMS) {
- $room_direction_state = $rooms[$i] -> {$k};
- if ($room_direction_state == $CLOSED) {
- $next_room = $i + 1;
- if ($next_room < $MAX_ROOMS) {
- $rooms[$i] -> {$k} = $next_room;
- $rooms[$next_room] -> {opposite($k)} = $i;
- }
- $i = $next_room;
- }
- }
- }
- }
- }
-}
-
-sub place_directional_object {
- my $room_number = shift;
- my $object = shift;
- my $direction;
-
- # Get a random direction and set the object in a random room direction.
- $direction = int(rand(3)) + 1;
- $rooms[$room_number] -> {$direction} = $object;
-
- # Update the room's link count
- ($rooms[$room_number] -> {'link_count'})++;
-
- if ($object == $GRUE) {
- $grue_location = $room_number;
- }
- elsif ($object == $EXIT) {
- $exit_location = $room_number;
- }
- elsif ($object == $PIT1) {
- $pit1_location = $room_number;
- }
- elsif ($object == $PIT2) {
- $pit2_location = $room_number;
- }
-}
-
-sub get_random_rooms {
- my $number_of_rooms = shift;
- my $random_num = 0;
- my %random_hash = ();
- my @room_numbers = ();
-
- for (my $i = 0; $i < $number_of_rooms;) {
- $random_num = int(rand($MAX_ROOMS));
-
- if (!$random_hash{$random_num}) {
- $random_hash{$random_num} = 1;
- $room_numbers[$i] = $random_num;
- $i++;
- }
- }
- return \@room_numbers;
-}
-
-sub set_state {
- my $set = shift;
- my $number_of_rooms;
- my $room_numbers;
-
- if ($grue_bagged) {
- $number_of_rooms = 5;
- }
- else {
- $number_of_rooms = 6;
- }
-
- $room_numbers = get_random_rooms($number_of_rooms);
-
- # Bats are non-directional objects and are placed in rooms. Since they are
- # non-directional they do not occupy a compass direction and do not add to the
- # room's link count. They are simply assigned to the Game State variables.
- $bat1_location = @$room_numbers[0];
- $bat2_location = @$room_numbers[1];
-
- # The Grue, Pits and Exit are directional objects, placed in rooms. They
- # are directional they occupy compass dirctions and are added to the room's
- # link count. In addition they are assigned to the Game State variables. The
- # Grue is only placed in a room again if it has not been bagged.
- place_directional_object(@$room_numbers[2], $PIT1);
- place_directional_object(@$room_numbers[3], $PIT2);
- place_directional_object(@$room_numbers[4], $EXIT);
- if (!$grue_bagged) {
- place_directional_object(@$room_numbers[5], $GRUE);
- }
- # If the game state is being set for the first time, then the player's
- # location is set to the same location as the Exit.
- if ($set) {
- $player_location = @$room_numbers[4];
- }
-}
-
-sub opposite {
- my $direction = shift;
-
- if ($direction == $NORTH) {
- return $SOUTH;
- }
- elsif ($direction == $SOUTH) {
- return $NORTH;
- }
- elsif ($direction == $EAST) {
- return $WEST;
- }
- elsif ($direction == $WEST) {
- return $EAST;
- }
- else {
- return 0;
- }
-}
-
-sub action_handler {
- my ($action, $sub_action) = @_;
- my $action_success = 0;
-
- if (defined $action) {
- $action = lc($action);
-
- # If the sub action (direction) is defined.
- if (defined $sub_action) {
- $sub_action = lc($sub_action);
-
- # If sub action (direction) is a valid direction.
- if (is_direction($sub_action)) {
- if ($sub_action eq 'q' || $action eq 'quit') {
- quit();
- }
- elsif ($action eq 'm' || $action eq 'move') {
- move_player($sub_action);
- $action_success = 1;
- }
- elsif ($action eq 's' || $action eq 'shoot') {
- shoot($sub_action);
- $action_success = 1;
- }
- }
-
- # If the sub action (direction) is not a valid direction.
- else {
- print "Enter 'N', 'S', 'E' OR 'W'.\n";
- if ($action eq 'm' || $action eq 'move') {
- move_player();
- }
- elsif ($action eq 's' || $action eq 'shoot') {
- shoot();
- }
- }
-
- if ($action_success) {
- my $random_num = 0;
- $random_num = int(rand($EARTHQUAKE_CHANCE));
-
- if ($random_num == $EARTHQUAKE) {
- print "\n<<<EARTHQUAKE>>>\n\n";
- randomize_rooms();
- set_state($RESET);
- connect_rooms();
- }
-
- describe_location();
- }
- }
- # If the sub action (direction) is not defined.
- else {
- if ($action eq 'm' || $action eq 'move') {
- move_player();
- }
- elsif ($action eq 's' || $action eq 'shoot') {
- shoot();
- }
- elsif ($action eq 'q' || $action eq 'quit') {
- quit();
- }
- }
- }
-}
-
-sub display_text {
- $| = 1;
- my $text = shift;
- for my $word (split/%/, $text) {
- print $word."\n";
- print 'Press enter when ready to continue: ';
- <STDIN>;
- }
- print "\033[2J";
- print "\033[0;0H";
- print "\t\t\t\t".'***********************'."\n";
- print "\t\t\t\t".'*** ***'."\n";
- print "\t\t\t\t".'*** GRUE HUNTER ***'."\n";
- print "\t\t\t\t".'*** ***'."\n";
- print "\t\t\t\t".'***********************'."\n\n";
-}
-
-sub read_input {
- my $action;
- my @actions;
- $action = <>;
- @actions = split(/\s+/, $action);
- return @actions;
-}
-
-sub is_direction {
- my $direction = shift;
- if (defined $direction) {
- return (
- $direction eq 'n' or
- $direction eq 'north' or
- $direction eq 's' or
- $direction eq 'south' or
- $direction eq 'e' or
- $direction eq 'east' or
- $direction eq 'w' or
- $direction eq 'west' or
- $direction eq 'q' or
- $direction eq 'quit'
- );
- }
- return '';
-}
-
-sub direction {
- my $direction = shift;
-
- if ($direction == $NORTH) {
- return 'north';
- }
- elsif ($direction == $SOUTH) {
- return 'south';
- }
- elsif ($direction == $EAST) {
- return 'east';
- }
- elsif ($direction == $WEST) {
- return 'west';
- }
- else {
- return '';
- }
-}
-
-sub move_player {
- my $sub_action = shift;
- my $direction;
- my $room_direction;
-
- if (defined $sub_action) {
- if ($sub_action eq 'n' || $sub_action eq 'north') {
- $direction = $NORTH;
- }
- elsif ($sub_action eq 's' || $sub_action eq 'south') {
- $direction = $SOUTH;
- }
- elsif ($sub_action eq 'e' || $sub_action eq 'east') {
- $direction = $EAST;
- }
- elsif ($sub_action eq 'w' || $sub_action eq 'west') {
- $direction = $WEST;
- }
-
- $room_direction = $rooms[$player_location] -> {$direction};
-
- # If the room that the player wants to move to is a room.
- if ($room_direction > $CLOSED) {
-
- # Check if the room has a bat.
- if ($room_direction == $bat1_location || $room_direction == $bat2_location) {
- print "Bats have you now.\n";
- print "They're lifting you up.\n";
- print "Ohhhh, where are we now?\n";
-
- my $number_of_rooms = 2;
- my $room_numbers = get_random_rooms($number_of_rooms);
-
- # Change player location (to where the bat dropped the player).
- $player_location = @$room_numbers[0];
-
- # Change location of offending bat.
- if ($room_direction == $bat1_location) {
- $bat1_location = @$room_numbers[1];
- }
- else {
- $bat2_location = @$room_numbers[1];
- }
- }
-
- # If free of bats, go ahead and move into the room.
- else {
- print "OK...\n";
- $player_location = $room_direction;
- }
- }
- elsif ($room_direction == $CLOSED) {
- print "You cannot go that way.\n";
- }
- elsif ($room_direction == $GRUE) {
- print "You bumped into the Grue.\n";
- print "He ate you before you could move.\n";
- quit();
- }
- elsif ($room_direction == $EXIT) {
- if ($grue_bagged) {
- print "You have reached the exit with your Grue.\n";
- print "Your supper will be filling tonight for sure!\n";
- }
- else {
- print "You have reached the exit without any Grue.\n";
- print "You are sure to starve.\n";
- }
- quit();
- }
- elsif ($room_direction < $PIT1) {
- my $pit_chance = int(rand($PIT_CHANCE));
- if ($pit_chance) {
- print "This room contains a bottomless pit. Part of the ground around it gave way when you entered and you almost fell in.\n";
- print "Be more careful next time!\n";
- $player_location = $room_direction;
- }
- else {
- print "You fell into a bottomless pit.\n";
- print "You fell a loooooong way...\n";
- quit();
- }
- }
- }
- else {
- my @actions;
- print "Move which way?\n";
- @actions = read_input();
- action_handler('m', $actions[0]);
- }
-}
-
-sub shoot {
- my $sub_action = shift;
- my $direction;
- my $room_direction;
-
- if (defined $sub_action) {
- if ($sub_action eq 'n' || $sub_action eq 'north') {
- $direction = $NORTH;
- }
- elsif ($sub_action eq 's' || $sub_action eq 'south') {
- $direction = $SOUTH;
- }
- elsif ($sub_action eq 'e' || $sub_action eq 'east') {
- $direction = $EAST;
- }
- elsif ($sub_action eq 'w' || $sub_action eq 'west') {
- $direction = $WEST;
- }
-
- $room_direction = $rooms[$player_location] -> {$direction};
-
- # If the arrow was shot in a direction without the Grue.
- if ($room_direction == $CLOSED) {
- print "Clunk!\n";
- print "The arrow bounced off the wall.\n";
- }
- # If the arrow was shot in a direction of the Grue.
- elsif ($room_direction == $GRUE) {
- my $shot = int(rand($EVEN_CHANCE));
- if ($shot) {
- print "You caught the Grue!\n";
- print "Now to find a way out...\n";
- $grue_bagged++;
-
- # Remove the Grue from the Room once bagged. Set the Grue
- # location to an undefined state (the Grue object -2).
- $rooms[$player_location] -> {$direction} = $CLOSED;
- ($rooms[$player_location] -> {'link_count'})--;
- $grue_location = $GRUE;
- }
- else {
- print "The arrow missed the Grue.\n";
- }
- }
- else {
- print "The arrow missed the Grue.\n";
- }
- }
- else {
- my @actions;
- print "Shoot which way?\n";
- @actions = read_input();
- action_handler('s', $actions[0]);
- }
-}
-
-sub describe_location {
- my $room_direction;
-
- print $rooms[$player_location] -> {'description'} . "\n";
-# print "Player Location: $player_location\n";
-# print "Grue Location: $grue_location\n";
-# print "Bat 1 Location: $bat1_location\n";
-# print "Bat 2 Location: $bat2_location\n";
-# print "Pit 1 Location: $pit1_location\n";
-# print "Pit 2 Location: $pit2_location\n";
-# print "Exit Location: $exit_location\n";
-
- print 'I see passages to the ';
- my @passages;
- for (my $i = 1; $i < 5; $i++) {
- $room_direction = $rooms[$player_location] -> {$i};
- if ($room_direction != $CLOSED) {
- push(@passages, direction($i));
- }
- }
-
- for (my $i = 0; $i < $#passages + 1; $i++) {
- if ($i == 0) {
- print $passages[$i];
- }
- elsif ($i < $#passages) {
- print ', ' . $passages[$i];
- }
- elsif ($i == $#passages) {
- print ' and ' . $passages[$i];
- }
- }
- print ". Choose wisely.\n";
-
- # If the Grue is in the player's current location.
- if ($player_location == $grue_location) {
- print "I smell the Grue!\n";
- }
- # If either pit is in the player's current location.
- elsif ($player_location == $pit1_location || $player_location == $pit2_location) {
- print "I feel a draft. A bottomless pit must be nearby. Be careful not to fall in.\n";
- }
- # If the Exit is in the player's current location, tell the player which
- # direction the Exit is located.
- elsif ($player_location == $exit_location) {
- for (my $i = 1; $i < 5; $i++) {
- $room_direction = $rooms[$player_location] -> {$i};
- if ($room_direction == $EXIT) {
- print 'The exit is to the ' . direction($i) . ".\n";
- }
- }
- }
- # Check the adjacent rooms for the Exit, Grue, Bats and Pits. Here
- # check whether the adjacent rooms are equivalent to the game state
- # variables for the Exit, Grue, Bats and Pits.
- else {
- for (my $i = 1; $i < 5; $i++) {
- $room_direction = $rooms[$player_location] -> {$i};
- if ($room_direction > $CLOSED) {
- if ($room_direction == $exit_location) {
- print "Exit nearby...\n";
- }
- elsif ($room_direction == $grue_location) {
- print "I smell the Grue!\n";
- }
- # If the room adjacent contains one of the two bats then warn the player.
- elsif ($room_direction == $bat1_location || $room_direction == $bat2_location) {
- print "I hear bats flying around in the distance. Be careful or they may grab you and carry you away.\n";
- }
- # If the room adjacent contains one of the two pits then warn the player.
- elsif ($room_direction == $pit1_location || $room_direction == $pit2_location) {
- print "I feel a draft. A bottomless pit must be nearby. Be careful not to fall in.\n";
- }
- }
- }
- }
-}
-
-sub quit {
- exit;
-}
-
-sub main {
- my @actions;
-
- display_text($instructions);
- randomize_rooms();
- set_state($SET);
- connect_rooms();
- describe_location();
- while (1) {
- print "Move or shoot?\n";
- @actions = read_input();
- action_handler(@actions);
- }
-}
-
-main();