--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Occupants;
use  Occupants;

package Creatures is
 type Creature is abstract new Occupant with private;
 type Creature_Access   is access Creature'Class;
private
 type Creature is abstract new Occupant with null record;
end Creatures;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Ada.Strings.Unbounded;
use  Ada.Strings.Unbounded;

package Directions is

 type Direction is (North, South, East, West, Up, Down);

 Reverse_Direction : constant array(Direction) of Direction :=
                    (North => South, South => North,
                     East =>West, West => East,
                     Up => Down, Down => Up);

 function To_Direction(Text : Unbounded_String) return Direction;
 -- Converts Text to Direction; raises Constraint_Error if it's not
 -- a legal direction.

 function Is_Direction(Text : Unbounded_String) return Boolean;
 -- Returns TRUE if Text is a direction, else false.

end Directions;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Occupants;
use  Occupants;

package Items is
 type Item     is new Occupant with private;
 type Item_Access       is access Item'Class;
 function May_I_Get(Direct_Object : access Item;
                    Agent : access Occupant'Class) return Boolean;

private
 type Item     is new Occupant with null record;

end Items;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Creatures;
use  Creatures;

package Monsters is
 type Monster is new Creature with private;
 type Monster_Access    is access Monster'Class;
private
 type Monster is new Creature with null record;
end Monsters;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Things, Directions;
use  Things, Directions;

package Occupants is

 -- An "Occupant" is a Thing that can be inside a Room or another Occupant.

 type Occupant is abstract new Thing with private;
 type Occupant_Access   is access all Occupant'Class;

 -- Dispatching subprograms:

 procedure Look(T : access Occupant);      -- Ask Occupant T to "look".

 procedure Get(Agent : access Occupant; Direct_Object : access Occupant'Class);
           -- Ask Agent to get Direct_Object.  This assumes that Agent can
           -- somehow access Direct_Object (i.e. is in the same room).
           -- If the agent decides that it can get the object, it will
           -- call May_I_Get to ask the object if that's okay.

 procedure Drop(Agent : access Occupant; Direct_Object : access Occupant'Class);
           -- Ask Agent to drop Direct_Object.

 procedure Inventory(Agent : access Occupant);
           -- Ask Agent to print a list of what Agent is carrying.

 procedure Go(Agent : access Occupant; Dir : in Direction);
            -- Ask Agent to go the given Direction Dir (North, South, etc.)

 procedure Put_View(T : access Occupant; Agent : access Thing'Class);
            -- Override Thing's Put_View.
 
 function May_I_Get(Direct_Object : access Occupant;
                    Agent : access Occupant'Class) return Boolean;
           -- Ask Direct_Object if "Agent" can get this object.
           -- Returns True if it's okay, else False.
           -- If the object does something while being gotten (or an attempt
           -- to do so) it does it in this call.

 function  May_I_Drop(Direct_Object : access Occupant;
                      Agent         : access Occupant'Class) return Boolean;
           -- Ask Direct_Object if "Agent" can drop this object;
           -- returns True if it's okay.

private

 type Occupant is abstract new Thing with
  record
    null;  -- Nothing here for now.
  end record;

end Occupants;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--
 
with Ada.Strings.Unbounded;
use  Ada.Strings.Unbounded;

package Parser is
 procedure Execute(Command : in Unbounded_String; Quit : out Boolean);
   -- Executes the given command.
   -- Sets Quit to True if the user may run additional commands.
end Parser;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Creatures;
use  Creatures;

package Players is
 type Player  is new Creature with private;
 type Player_Access     is access Player'Class;
private
 type Player  is new Creature with null record;
end Players;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Things, Directions;
use  Things, Directions;

package Rooms is
 type Room     is new Thing with private;
 type Room_Access       is access all Room'Class;

 procedure Put_View(T : access Room; Agent : access Thing'Class);

 procedure Connect(Source : access Room; Dir : in Direction; 
                   Destination : access Thing'Class;
                   Bidirectional : in Boolean := True);
  -- Create a connection from Source to Destination in Direction Dir.
  -- If it's bidirectional, create another connection the reverse way.

 procedure Disconnect(Source : access Room; Dir : in Direction; 
                      Bidirectional : in Boolean := True);
 -- Reverse of connect; disconnects an existing connection, if any.

 function What_Is(From : access Room; Dir : in Direction) return Thing_Access;
 -- Returns what is at direction "Dir" from "From".
 -- Returns null if nothing connected in that direction.

private

 type Destination_Array is array(Direction) of Thing_Access;

 type Room     is new Thing with
  record
    Destinations : Destination_Array;
  end record;

end Rooms;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Ada.Strings.Unbounded, Ada.Finalization, Directions;
use  Ada.Strings.Unbounded, Ada.Finalization, Directions;

package Things is

 -- "Thing" is the root class for all things in this small world.
 -- Rooms, Players, Items, and Monsters are derived from Thing.

 
 type Thing is abstract new Limited_Controlled with private;
 type Thing_Access is access all Thing'Class;

 type Article_Type is (A, An, The, Some, None);

 -- Public Dispatching operations.

 procedure Put_View(T : access Thing; Agent : access Thing'Class) is abstract;
  -- Put what Agents sees inside T.

 function What_Is(From : access Thing; Dir : in Direction) return Thing_Access;
 -- Returns what is at direction "Dir" from "From".
 -- Returns null if nothing connected in that direction.

 -- Public non-Dispatching operations:

 procedure Set_Name(T : access Thing'Class; Article : in Article_Type;
                    Name : in Unbounded_String);
 procedure Set_Name(T : access Thing'Class; Article : in Article_Type;
                    Name : in String);
 function Name(T : access Thing'Class) return Unbounded_String;
 pragma Inline(Name);

 function Short_Description(T : access Thing'Class) return Unbounded_String;
 -- Returns Article + Name, i.e. "the box", "a car", "some horses".
 
 procedure Set_Description(T : access Thing'Class;
                           Description : in Unbounded_String);
 procedure Set_Description(T : access Thing'Class;
                           Description : in String);
 function Long_Description(T : access Thing'Class) return Unbounded_String;
 
 procedure Place(T : access Thing'Class; Into : Thing_Access);
   -- Place T inside "Into" (removing it from wherever it was).
   -- Attempting to place T into itself will print an error message
   -- and fail.
   -- The second parameter is Thing_Access, not Thing'Class, because
   -- "null" is a valid value for "Into".
 function Container(T : access Thing'Class) return Thing_Access;
   -- Return access value to the container of T.
 function Has_Contents(T : access Thing'Class) return Boolean;
   -- Does T have anything in it?

 function Find(Agent : access Thing'Class;
               Object_Name : in Unbounded_String) return Thing_Access;
          -- Find the given Object_Name in the same container as the agent.
          -- Prints and error message and returns null if not found.

 function Find_Inside(Agent       : access Thing'Class;
                      Object_Name : in Unbounded_String)
          return Thing_Access;
          -- Find the given Object_Name inside the agent.
          -- Prints and error message and returns null if not found.

 procedure Put_Contents(T : access Thing'Class;
                        Ignore : access Thing'Class;
                        Heading_With_Contents : in String;
                        Heading_Without_Contents : in String := "");
   -- Put a description of the contents of T.
   -- Act as though "Ignore" isn't there.
   -- If there is something, print Heading_With_Contents;
   -- If there isn't something, print Heading_Without_Contents.

 procedure Sorry(Prohibited_Operation : String;
                 Prohibited_Direct_Object : Unbounded_String);
   -- Put "Sorry, you may not XXX the YYY".


private

 type Thing is abstract new Limited_Controlled with
  record
   Name, Description : Unbounded_String;
   Article           : Article_Type := A;
   Container         : Thing_Access; -- what Thing contains me?
   Next_Sibling      : Thing_Access; -- next Thing in my container.
   First_Containee   : Thing_Access; -- first Thing inside me.
  end record;

end Things;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Text_IO, Ada.Strings.Unbounded;
use  Text_IO, Ada.Strings.Unbounded;

package Ustrings is

  -- This package provides a simpler way to work with type
  -- Unbounded_String, since this type will be used very often.
  -- Most users will want to ALSO with "Ada.Strings.Unbounded".
  -- Ideally this would be a child package of "Ada.Strings.Unbounded".
  --

  -- This package provides the following simplifications:
  --  + Shortens the type name from "Unbounded_String" to "Ustring".
  --  + Creates shorter function names for To_Unbounded_String, i.e.
  --    To_Ustring(U) and U(S).  "U" is not a very readable name, but
  --    it's such a common operation that a short name seems appropriate
  --    (this function is needed every time a String constant is used).
  --    It also creates S(U) as the reverse of U(S).
  --  + Adds other subprograms, currently just "Swap".
  --  + Other packages can use this package to provide other simplifications.

  subtype Ustring is Unbounded_String;

  function To_Ustring(Source : String)  return Unbounded_String
                                         renames To_Unbounded_String;
  function U(Source : String)           return Unbounded_String
                                         renames To_Unbounded_String;
  function S(Source : Unbounded_String) return String
                                         renames To_String;

  -- "Swap" is important for reuse in some other packages, so we'll define it.

  procedure Swap(Left, Right : in out Unbounded_String);


  function Empty(S : Unbounded_String) return Boolean;
   -- returns True if Length(S)=0.
  pragma Inline(Empty);


  -- I/O Routines.
  procedure Get_Line(File : in File_Type; Item : out Unbounded_String);
  procedure Get_Line(Item : out Unbounded_String);

  procedure Put(File : in File_Type; Item : in Unbounded_String);
  procedure Put(Item : in Unbounded_String);

  procedure Put_Line(File : in File_Type; Item : in Unbounded_String);
  procedure Put_Line(Item : in Unbounded_String);

end Ustrings;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Occupants;
use  Occupants;

package World is

 procedure Setup;
  -- Setup the World; initialize the contents of the world.


 function Me return Occupant_Access;
           -- Return an access variable pointing to the current player.

end World;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Ada.Characters.Handling;
use  Ada.Characters.Handling;

package body Directions is

 Abbreviations : constant String := "nsewud";

 procedure To_Direction(Text : in Unbounded_String;
                        Is_Direction : out Boolean;
                        Dir  : out Direction) is
  Lower_Text : String := To_Lower(To_String(Text));
  -- Attempt to turn "Text" into a direction.
  -- If successful, set "Is_Direction" True and "Dir" to the value.
  -- If not successful, set "Is_Direction" False and "Dir" to arbitrary value.
 begin
   if Length(Text) = 1 then
     -- Check if it's a one-letter abbreviation.
     for D in Direction'Range loop
       if Lower_Text(1) = Abbreviations(Direction'Pos(D) + 1) then
         Is_Direction := True;
         Dir := D;
         return;
       end if;
     end loop;
     Is_Direction := False;
     Dir := North;
     return;

   else
     -- Not a one-letter abbreviation, try a full name.
     for D in Direction'Range loop
       if Lower_Text = To_Lower(Direction'Image(D)) then
         Is_Direction := True;
         Dir := D;
         return;
       end if;
     end loop;
     Is_Direction := False;
     Dir := North;
     return;
   end if;
 end To_Direction;

 function To_Direction(Text : in Unbounded_String) return Direction is
   Is_Direction : Boolean;
   Dir          : Direction;
 begin
   To_Direction(Text, Is_Direction, Dir);
   if Is_Direction then
      return Dir;
   else
      raise Constraint_Error;
   end if;
 end To_Direction;

 function Is_Direction(Text : in Unbounded_String) return Boolean is
   Is_Direction : Boolean;
   Dir          : Direction;
 begin
   To_Direction(Text, Is_Direction, Dir);
   return Is_Direction;
 end Is_Direction;

end Directions;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

package body Items is

 function May_I_Get(Direct_Object : access Item;
                    Agent : access Occupant'Class) return Boolean is
 begin
  return True;
 end May_I_Get;

end Items;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Text_IO, Ada.Strings.Unbounded, Ustrings, Rooms;
use  Text_IO, Ada.Strings.Unbounded, Ustrings, Rooms;

package body Occupants is


 procedure Put_View(T : access Occupant; Agent : access Thing'Class) is
 begin
  Put("You are inside ");
  Put_Line(Short_Description(T));
  Put_Line(".");
  Put_Contents(T, Agent, "You see:");
 end Put_View;

 procedure Look(T : access Occupant) is
 -- T is running a "look" command; tell T what he views.
 begin
  if Container(T) = null then
    Put("You are inside nothing at all.");
  else
    Put_View(Container(T), T);
  end if;
 end Look;


 procedure Get(Agent : access Occupant; Direct_Object : access Occupant'Class)
 is
 begin
   if May_I_Get(Direct_Object, Agent) then
     Place(T => Direct_Object, Into => Thing_Access(Agent));
   end if;
 end Get;
 
 function May_I_Get(Direct_Object : access Occupant;
                    Agent : access Occupant'Class)
          return Boolean is
 begin
   Sorry("get", Name(Direct_Object));  -- Tell the getter sorry, can't get it
   return False;
 end May_I_Get;
 
 procedure Drop(Agent : access Occupant;
                Direct_Object : access Occupant'Class) is
 begin
   if May_I_Drop(Direct_Object, Agent) then
     Place(T => Direct_Object, Into => Container(Agent));
   end if;
 end Drop;

 function  May_I_Drop(Direct_Object : access Occupant;
                      Agent : access Occupant'Class)
           return Boolean is
 begin
   return True;
 end May_I_Drop;
 

 procedure Inventory(Agent : access Occupant) is
 begin
  Put_Contents(Agent, Agent,
               "You're carrying:",
               "You aren't carrying anything.");
 end Inventory;

 procedure Go(Agent : access Occupant; Dir : in Direction) is
 begin
  if Container(Agent) = null then
    Put_Line("Sorry, you're not in a room!");
  else
    declare
      Destination : Thing_Access := What_Is(Container(Agent), Dir);
    begin
     if Destination = null then
       Put_Line("Sorry, you can't go that way.");
     else
       Place(Agent, Destination);
     end if;
    end;
  end if;
 end Go;
 
end Occupants;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Text_IO, Ada.Strings.Maps.Constants, Ustrings, Things, Occupants, World;
use  Text_IO, Ada.Strings.Maps.Constants, Ustrings, Things, Occupants, World;
use  Ada.Strings, Ada.Strings.Maps;

with Directions;
use  Directions;

package body Parser is

 Spaces : constant Character_Set := To_Set(' ');

 procedure Split(Source     : in  Unbounded_String;
                 First_Word : out Unbounded_String;
                 Rest       : out Unbounded_String) is
  First : Positive; -- Index values of first word.
  Last  : Natural;
 -- Puts first word of Source into First_Word, the rest of the words in Rest
 -- (without leading spaces); words are separated by one or more spaces;
 -- if there are no spaces, Rest returns empty.
 begin
  Find_Token(Source, Spaces, Outside, First, Last);
  First_Word := U(Slice(Source, First, Last));
  Rest       := Trim(U(Slice(Source, Last + 1, Length(Source))), Left);
 end Split;



 procedure Execute(Command : in Unbounded_String; Quit : out Boolean) is
  Trimmed_Command : Unbounded_String := Trim(Command, Both);
  Verb, Arguments, First_Argument, Rest_Of_Arguments : Unbounded_String;
  Direct_Object : Occupant_Access;
 begin
  Quit := False; -- By default assume we won't quit.
  if (Empty(Trimmed_Command)) then
    return;      -- Ignore blank lines.
  end if;

  -- Extract Verb and First_Argument and force them to lower case.
  Split(Trimmed_Command, Verb, Arguments);
  Translate(Verb, Lower_Case_Map);
  Split(Arguments, First_Argument, Rest_Of_Arguments);
  Translate(First_Argument, Lower_Case_Map);


  -- Try to execute "Verb".

  if    Verb = "look" then
    Look(Me);
  elsif Verb = "get" then
    Direct_Object := Occupant_Access(Find(Me, First_Argument));
    if Direct_Object /= null then
      Get(Me, Direct_Object);
    end if;
  elsif Verb = "drop" then
    Direct_Object := Occupant_Access(Find_Inside(Me, First_Argument));
    if Direct_Object /= null then
      Drop(Me, Direct_Object);
    end if;
  elsif Verb = "inventory" or Verb = "inv" then
    Inventory(Me);
  elsif Verb = "quit" then
    Quit := True;
  elsif Verb = "go" and then Is_Direction(First_Argument) then
    Go(Me, To_Direction(First_Argument));
    Look(Me);
  elsif Is_Direction(Verb) then  -- Is the verb a direction (north, etc)?
    Go(Me, To_Direction(Verb));
    Look(Me);
  elsif Verb = "help" then
    Put_Line("Please type in one or two word commands, beginning with a verb");
    Put_Line("or direction. Directions are north, south, east, west, etc.");
    Put_Line("Here are some sample commands:");
    Put_Line("look, get box, drop box, inventory, go west, west, w, quit.");
  else
   Put_Line("Sorry, I don't recognize that verb. Try 'help'.");
  end if;
  
 end Execute;
end Parser;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Text_IO, Ustrings;
use  Text_IO, Ustrings;

package body Rooms is

 procedure Connect(Source : access Room; Dir : in Direction; 
                   Destination : access Thing'Class;
                   Bidirectional : in Boolean := True) is
 begin
   Source.Destinations(Dir) := Thing_Access(Destination);
   if Bidirectional then  -- Connect in reverse direction.
     Room_Access(Destination).Destinations(Reverse_Direction(Dir)) := 
              Thing_Access(Source);
   end if;
 end Connect;

 procedure Disconnect(Source : access Room; Dir : in Direction; 
                      Bidirectional : in Boolean := True) is
 begin
   if Bidirectional then
     -- If it's bidirectional, remove the other direction. The following "if"
     -- statement, if uncommented, checks to make sure that
     -- disconnecting a bidirectional link only happens to a Room.
     -- if (Source.Destinations(Dir).all'Tag in Room'Class) then
       Room_Access(Source.Destinations(Dir)).
                   Destinations(Reverse_Direction(Dir)) := null;
     -- end if;
   end if;
   Source.Destinations(Dir) := null;
 end Disconnect;

 function What_Is(From : access Room; Dir : in Direction) return Thing_Access is
 begin
  return From.Destinations(Dir);
 end What_Is;

 procedure Put_View(T : access Room; Agent : access Thing'Class) is
 begin
  Put("You are ");
  Put(Long_Description(T));
  Put_Line(".");
  Put_Contents(T, Agent, "You see:");
 end Put_View;

end Rooms;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 

-- Main routine to start up "Small", a small text adventure game to
-- demonstrate Ada 95.

--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

-- For documentation see the following URL:
--   http://www.adahome.com//Tutorials/Lovelace/small.htm

with Text_IO, Ada.Strings.Unbounded, Ustrings, World;
use  Text_IO, Ada.Strings.Unbounded, Ustrings;

with Parser;

procedure Small is
  Command : Unbounded_String; -- Contains user's current command.
  Quit    : Boolean := False;
begin
 Put_Line("Welcome to a Small World!");

 World.Setup;

 while not Quit loop
  New_Line;
  Put_Line("Your Command?");
  Get_Line(Command);
  Parser.Execute(Command, Quit);
 end loop;

 Put_Line("Bye!");
end Small;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Text_IO, Ustrings;
use  Text_IO, Ustrings;


package body Things is

 -- Define basic types for the world and their operations.


 -- Supporting Subprograms:

 procedure Sorry(Prohibited_Operation : String;
                 Prohibited_Direct_Object : Unbounded_String) is
 begin
  Put_Line("Sorry, you may not " & Prohibited_Operation & " the " &
           S(Prohibited_Direct_Object));
 end Sorry;


 -- Routines to manipulate First_Containee, Next_Sibling, Container:

 function Previous_Sibling(Containee : access Thing'Class)
          return Thing_Access is
  -- Find the previous sibling of containee.  It's an error to call
  -- this if Containee has no previous sibling.
    Current : Thing_Access := Containee.Container.First_Containee;
 begin
    while Current.Next_Sibling /= Thing_Access(Containee) loop
      Current := Current.Next_Sibling;
    end loop;
    return Current;
 end Previous_Sibling;

 function Last_Containee(Container : access Thing'Class)
          return Thing_Access is
   -- Return an access value of the last contained Thing in container.
   -- It's an error to call this routine if there are no containees.
    Current : Thing_Access := Container.First_Containee;
 begin
    while Current.Next_Sibling /= null loop
      Current := Current.Next_Sibling;
    end loop;
    return Current;
 end Last_Containee;

 procedure Remove(Containee : access Thing'Class) is
 -- Remove Containee from its current Container.
  Previous_Thing : Thing_Access;
 begin
  if Containee.Container /= null then
    if Containee.Container.First_Containee = Thing_Access(Containee) then
       -- Containee is the first Thing in its container.
       Containee.Container.First_Containee := Containee.Next_Sibling;
    else
       Previous_Thing := Previous_Sibling(Containee);
       Previous_Thing.Next_Sibling := Containee.Next_Sibling;
    end if;
    Containee.Next_Sibling := null;
    Containee.Container    := null;
  end if;
 end Remove;


 procedure Place(T : access Thing'Class; Into : Thing_Access) is
 -- Place "T" inside "Into".
  Last : Thing_Access;
 begin
  if (Thing_Access(T) = Into) then
    Put_Line("Sorry, that can't be done.");
    return;
  end if;
  Remove(T); -- Remove Thing from where it is now.
  if Into /= null then
    if Into.First_Containee = null then
      Into.First_Containee := Thing_Access(T);
    else
      Last := Last_Containee(Into);
      Last.all.Next_Sibling := Thing_Access(T);
    end if;
  end if;
  T.Container := Into;
 end Place;

 procedure Put_Contents(T : access Thing'Class;
                        Ignore : access Thing'Class;
                        Heading_With_Contents : in String;
                        Heading_Without_Contents : in String := "") is
   -- Put a description of the contents of T.
   -- If there is something, print Heading_With_Contents;
   -- If there isn't something, print Heading_Without_Contents.
   -- Ignore The_Player, since presumably the player already knows about
   -- him/herself.
   Current : Thing_Access := T.First_Containee;
   Have_Put_Something : Boolean := False;
 begin
  while Current /= null loop
    if Current /= Thing_Access(Ignore) then
      -- This what we're to ignore, print it out.
      if Have_Put_Something then
        Put(", ");
      else
        -- We're about to print the first item; print the heading.
        Put_Line(Heading_With_Contents);
      end if;
      Put(Short_Description(Current));
      Have_Put_Something := True;
    end if;
    Current := Current.Next_Sibling;
  end loop;
  if Have_Put_Something then
    Put_Line(".");
  elsif Heading_With_Contents'Length > 0 then
    Put_Line(Heading_Without_Contents);
  end if;
 end Put_Contents;


 -- Dispatching Operations:

 function What_Is(From : access Thing; Dir : in Direction)
          return Thing_Access is
 begin
   return null; -- As a default, you can't go ANY direction from "here".
 end What_Is;


 -- Non-dispatching public operations:

 procedure Set_Name(T : access Thing'Class; Article : in Article_Type;
                    Name : in Unbounded_String) is
 begin
   T.Article := Article;
   T.Name    := Name;
 end Set_Name;

 procedure Set_Name(T : access Thing'Class; Article : in Article_Type;
                    Name : in String) is
 begin
   T.Article := Article;
   T.Name    := To_Unbounded_String(Name);
 end Set_Name;

 function Name(T : access Thing'Class) return Unbounded_String is
 begin
  return T.Name;
 end Name;

 procedure Set_Description(T : access Thing'Class;
                           Description : in Unbounded_String) is
 begin
  T.Description := Description;
 end Set_Description;

 procedure Set_Description(T : access Thing'Class;
                           Description : in String) is
 begin
  T.Description := To_Unbounded_String(Description);
 end Set_Description;

 function Long_Description(T : access Thing'Class) return Unbounded_String is
 begin
   return T.Description;
 end Long_Description;
 

 -- Eventually we'll use an array for the article, but a minor GNAT 2.7.0 bug
 -- will cause this to raise a Segmentation Fault when the program quits:
 -- Article_Text : constant array(Article_Type) of Unbounded_String :=
 --     (A => U("a "), An => U("an "), The => U("the "), Some => U("some "),
 --      None => U(""));

 function Short_Description(T : access Thing'Class) return Unbounded_String is
 begin
  case T.Article is
   when A    => return "a "    & T.Name;
   when An   => return "an "   & T.Name;
   when The  => return "the "  & T.Name;
   when Some => return "some " & T.Name;
   when None => return           T.Name;
  end case;
  -- Should become return Article_Text(T.Article) & T.Name;
 end Short_Description;

 function Find(Agent : access Thing'Class;
               Object_Name : in Unbounded_String) return Thing_Access is
 begin
   if Agent.Container = null then
     Put_Line("You aren't in anything.");
     return null;
   else
     return Find_Inside(Agent.Container, Object_Name);
   end if;
 end Find;

 function Find_Inside(Agent : access Thing'Class;
                      Object_Name : in Unbounded_String)
          return Thing_Access is
   Current : Thing_Access := Agent.First_Containee;
 begin
   if Empty(Object_Name) then
     Put_Line("Sorry, you need to name an object.");
     return null;
   end if;
   while Current /= null loop
     if Current.Name = Object_Name then
       return Current;
     end if;
     Current := Current.Next_Sibling;
   end loop;
   Put("Sorry, I don't see a ");
   Put_Line(Object_Name);
   return null;
 end Find_Inside;

 function Container(T : access Thing'Class) return Thing_Access is
 begin
   return T.Container;
 end Container;

 function Has_Contents(T : access Thing'Class) return Boolean is
 begin
   if T.First_Containee = null then
     return False;
   else
     return True;
   end if;
 end Has_Contents;

end Things;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

package body Ustrings is

  Input_Line_Buffer_Length : constant := 1024;
    -- If an input line is longer, Get_Line will recurse to read in the line.


  procedure Swap(Left, Right : in out Unbounded_String) is
    -- Implement Swap.  This is the portable but slow approach.
    Temporary : Unbounded_String;
  begin
    Temporary := Left;
    Left := Right;
    Right := Temporary;
  end Swap;

  function Empty(S : Unbounded_String) return Boolean is
   -- returns True if Length(S)=0.
  begin
   return (Length(S) = 0);
  end Empty;
 

  -- Implement Unbounded_String I/O by calling Text_IO String routines.


  -- Get_Line gets a line of text, limited only by the maximum number of
  -- characters in an Unbounded_String.  It reads characters into a buffer
  -- and if that isn't enough, recurses to read the rest.

  procedure Get_Line (File : in File_Type; Item : out Unbounded_String) is

    function More_Input return Unbounded_String is
       Input : String (1 .. Input_Line_Buffer_Length);
       Last  : Natural;
    begin
       Get_Line (File, Input, Last);
       if Last < Input'Last then
          return   To_Unbounded_String (Input(1..Last));
       else
          return   To_Unbounded_String (Input(1..Last)) & More_Input;
       end if;
    end More_Input;

  begin
      Item := More_Input;
  end Get_Line;


  procedure Get_Line(Item : out Unbounded_String) is
  begin
    Get_Line(Current_Input, Item);
  end Get_Line;

  procedure Put(File : in File_Type; Item : in Unbounded_String) is
  begin
    Put(File, To_String(Item));
  end Put;

  procedure Put(Item : in Unbounded_String) is
  begin
    Put(Current_Output, To_String(Item));
  end Put;

  procedure Put_Line(File : in File_Type; Item : in Unbounded_String) is
  begin
    Put(File, Item);
    New_Line(File);
  end Put_Line;

  procedure Put_Line(Item : in Unbounded_String) is
  begin
    Put(Current_Output, Item);
    New_Line;
  end Put_Line;

end Ustrings;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
--
-- Copyright (C) 1996 Ada Resource Association (ARA), Columbus, Ohio.
-- Author: David A. Wheeler
--

with Text_IO, Ada.Strings.Unbounded, Ustrings;
use  Text_IO, Ada.Strings.Unbounded, Ustrings;

with Things, Players, Items, Rooms, Directions;
use  Things, Players, Items, Rooms, Directions;

package body World is

 The_Player : Player_Access;    -- This is the object representing the
                                -- current player.


 procedure Setup is
   Starting_Room : Room_Access := new Room;
   Box           : Item_Access := new Item;
   Knife         : Item_Access := new Item;
   Living_Room   : Room_Access := new Room;
 begin
   Set_Name(Starting_Room, The, "Hallway");
   Set_Description(Starting_Room, "in the hallway. There is a living room " &
                   "to the west");

   Set_Name(Box, A, "box");
   Set_Description(Box, "a red box");
   Place(T => Box, Into => Thing_Access(Starting_Room));

   Set_Name(Knife, A, "knife");
   Set_Description(Box, "a black knife");
   Place(T => Knife, Into => Thing_Access(Starting_Room));

   Set_Name(Living_Room, The, "Living Room");
   Set_Description(Living_Room, "in the living room. " &
                                "A hallway is to your east");
   Connect(Starting_Room, West, Living_Room);

   -- Setup player.
   The_Player := new Player; 
   Set_Name(The_Player, None, "Fred");
   Set_Description(The_Player, Name(The_Player));
   Place(T => Me,  Into => Thing_Access(Starting_Room));
   Look(Me);

 end Setup;


 function Me return Occupant_Access is
  -- Return access value to current player.
 begin
  return Occupant_Access(The_Player);
 end Me;

end World;

--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright and authorship notice appear in all
-- copies and that both that copyright notice and this permission notice
-- appear in supporting documentation.
-- 
-- The ARA makes no representations about the suitability of this software
-- for any purpose.  It is provided "as is" without express
-- or implied warranty.
-- 
