RCRPG/PureBasic

From Rosetta Code
RCRPG/PureBasic is part of RCRPG. You may find other members of RCRPG at Category:RCRPG.

This version is a more or less direct translation of the version done in Perl (See RCRPG/Perl) with some additional functionality added. Commands include attack, drop, take, equip, alias, name, inventory, and directions. The 'name' command can use a double-quoted string if spaces wanted to be included in the name (i.e. 'name "this room").

An additional command 'ask' was implemented for providing hints in finding the randomly placed Prize Room. The program also allows the stacking of commands if they are separated by a space (i.e. 'take all equip sledge a north n' will perform four actions: take all, equip sledge, [a]ttack north, [n]orth).

A simple outlay of the play strategy is: Take and equip a sledge. Get some gold to ask for hints (from the program) and move a ladder into a room to go up if necessary and attack walls to make exits.

;Here is a description of all of the commands:
;
;north
;south
;east
;west
;up
;down
;
;Move in the direction specified. The player won’t be able to move if there isn’t
;an exit in that direction.
;
;attack (direction)
;
;Attack in the direction specified. (Equip the sledge first.)
;
;drop {all|(item name)}
;
;Drop the item specified. Or drop everything the player is carrying.
;
;take {all|(item name)}
;
;Take the item specified. Or take everything in the room.
;
;inventory
;
;Display everything the player is carrying.
;
;name (name)
;
;The player can rename the room with this command.
;
;equip (item name)
;
;Equip the item in question.
;
;alias (existing command name) (new command name)
;
;Give an existsing command an alias. Starting aliases are n,s,e,w,u,d. i and inv
;are the same as inventory, a is the same as attack.
;
;At the start of the game, only two rooms exist, the Start and the Prize room.
;All other rooms the player creates by moving around.  The Prize room is randomly
;located each game.  If you need a hint just 'ask'.
;
;To move out of the start room, the player needs to break a hole in a wall,
;ceiling or floor. The sledge is needed to do this.
;
;There are three kinds of items in the different rooms: sledges, gold and
;ladders. A sledge is needed to gain access to rooms the player doesn’t have
;access to. A ladder is needed to move between levels.
;
;Every room, except the prize room, may randomly have gold, a sledge and/or a
;ladder. The player will have to leave the ladder behind when they go up a level,
;so they may have to hunt around a bit for a new ladder.
;
;When the player finds a ladder, they can either move up a level from that room,
;or they can take the ladder and drop it in a room that’s more convenient.
;
;The player can make their way to the Prize Room but there's nothing
;useful there; just gold.
;----------
Structure coord3D
  x.i
  y.i
  z.i
EndStructure

Structure room
  location.coord3D
  name.s
  description.s
  List items.i()
  exits.i ;bitmapped
EndStructure 

Structure player
  *curRoomPtr.room
  location.coord3D
  List inventory.i()
  equipped.i
EndStructure

Enumeration ;items
  #itm_any = -1
  #itm_nothing = 0
  #itm_sledge
  #itm_ladder
  #itm_gold
  #itm_count
EndEnumeration

Enumeration ;directions
  #dir_Nowhere
  #dir_North
  #dir_South
  #dir_East
  #dir_West
  #dir_Up
  #dir_Down
  #dir_count
EndEnumeration

Enumeration ;console colors
  #con_Black
  #con_Blue
  #con_Green
  #con_Cyan
  #con_Red
  #con_Magenta
  #con_Brown
  #con_Light_Grey
  #con_Dark_Grey
  #con_Bright_Blue
  #con_Bright_Green
  #con_Bright_Cyan
  #con_Bright_Red
  #con_Bright_Magenta
  #con_Yellow
  #con_White
EndEnumeration
#ConsoleWidth = 79 ;character width of console

Global NewMap *locations.room() ;"xx,yy,zz" hash of locations and their associated room data
Global NewList rooms.room() ;room data
Prototype.s command_prt(cmd.s) ;each command acts on an input string and returns the unused portion
Global NewMap *commands() ;hash of commands and their functions, all use the same prototype
Global quitNow = #False
Global isConsoleAvailable = #False
Global player.player
Global prizeRoom.coord3D
Global hintCount
Global foundPrizeRoom = #False

;==========================
DataSection
  dirData:
  Data.i #dir_Nowhere: Data.s "nowhere", "nowhere", "to nowhere": Data.i  0,  0,  0
  Data.i #dir_North  : Data.s "north", "south", "to the north"  : Data.i  0,  1,  0
  Data.i #dir_South  : Data.s "south", "north", "to the south"  : Data.i  0, -1,  0
  Data.i #dir_East   : Data.s "east", "west", "to the east"     : Data.i  1,  0,  0
  Data.i #dir_West   : Data.s "west", "east", "to the west"     : Data.i -1,  0,  0
  Data.i #dir_Up     : Data.s "up", "down", "in the ceiling"    : Data.i  0,  0,  1
  Data.i #dir_Down   : Data.s "down", "up", "in the floor"      : Data.i  0,  0, -1
  
  roomDescription:
  Data.s "featureless room", "cold dark room", "very stinky room", "small but comfortable room"
  Data.s "room with an incredible echo! Yahollaydoo!", "room resembling a window-less jail"
  Data.s "green room", "black room", "blue room", "brown sad room", "room which breathes pain"
  Data.s "mysterious room", "yellow room", "room with a think layer of dust"
EndDataSection
#count_roomDescriptions = 14 ;count of room Descriptions

Structure dirText
  name.s
  reverseName.s
  exitDesc.s
EndStructure

Global Dim dirText.dirText(#dir_count - 1) ;direction text for each direction index
Global NewMap dirIndex.i(#dir_count - 1) ;index for each name
Global NewMap directions.coord3D() ;coordinate offsets
Define i, x
For i = 0 To #dir_count - 1
  Read.i x
  Read.s dirText(x)\name
  Read.s dirText(x)\reverseName
  Read.s dirText(x)\exitDesc
  
  dirIndex(dirText(x)\name) = x
  
  Read.i directions(dirText(x)\name)\x
  Read.i directions(dirText(x)\name)\y
  Read.i directions(dirText(x)\name)\z
Next 

;============================
Global Dim itemName.s(#itm_count - 1) ;item names for each item index
itemName(#itm_nothing) = "nothing"
itemName(#itm_sledge) = "sledge"
itemName(#itm_ladder) = "ladder"
itemName(#itm_gold) = "gold"

;============================
Declare setLocation(*location.coord3D, x, y, z)

*locations("0,0,0") = AddElement(rooms())
rooms()\name = "The Start"
setLocation(rooms()\location, 0, 0, 0)
rooms()\description = "simple room"
AddElement(rooms()\items())
rooms()\items() = #itm_sledge

setLocation(prizeRoom, Random(6) - 3, Random(6) - 3, Random(5) + 2)
*locations(Str(prizeRoom\x) + "," + Str(prizeRoom\y) + "," + Str(prizeRoom\z)) = AddElement(rooms())
rooms()\name = "The Prize Room"
rooms()\location = prizeRoom
rooms()\description = "very bright and shiny metallic room"
For i = 1 To 9
  AddElement(rooms()\items())
  rooms()\items() = #itm_gold
Next 

Procedure setLocation(*location.coord3D, x, y, z)
  *location\x = x
  *location\y = y
  *location\z = z
EndProcedure

Procedure.s shift(cmd.s, count = 1, sep.s = " ")
  While count > 0
    Protected x = FindString(cmd, sep, 1)
    If x
      cmd = Right(cmd, Len(cmd) - x)
    Else
      cmd = ""
    EndIf
    count - 1
  Wend   
  ProcedureReturn Trim(cmd)
EndProcedure

Procedure display(text.s, textColor = #con_Bright_Cyan, backColor = #con_Black)
  Static lastPrintPos = 0
  If isConsoleAvailable
    Protected lineCount, curLine = 1, lineText.s, maxWidth, wrapPos, outputText.s
    ConsoleColor(textColor, backColor)
    
    lineCount = CountString(text, #CR$)
    Repeat
      lineText = StringField(text, curLine, #CR$)
      While (lastPrintPos + Len(lineText)) >  #ConsoleWidth
        maxWidth = #ConsoleWidth - lastPrintPos
        wrapPos = maxWidth - FindString(ReverseString(Left(lineText, maxWidth)), " ", 1) ;wrap at space
        outputText = Left(lineText, wrapPos)
        If wrapPos <> maxWidth
          ;find last of a group of spaces
          Repeat
            wrapPos + 1
          Until Mid(lineText, wrapPos + 1, 1) <> " "
        ElseIf maxWidth <> #ConsoleWidth And FindString(lineText, " ", 1) <= #ConsoleWidth
          wrapPos = 0
          outputText = ""
        EndIf 
        
        PrintN(outputText)
        lineText = Mid(lineText, wrapPos + 1)
        lastPrintPos = 0
      Wend 
      
      If lineCount > 0
        PrintN(lineText)
        lastPrintPos = 0
      Else
        Print(lineText)
        lastPrintPos + Len(lineText)
      EndIf 
      lineCount - 1: curLine + 1
    Until lineCount < 0
  EndIf 
  ProcedureReturn lastPrintPos
EndProcedure

Procedure displayN(text.s, textColor = #con_Bright_Cyan, backColor = #con_Black)
  ProcedureReturn display(text + #CR$, textColor, backColor)
EndProcedure

Procedure decodeExits(Array dir(1), exits)
  Protected i, count
  Dim dir(#dir_count - 1)
  
  For i = 1 To #dir_count
    If exits & 1
      dir(count) = i
      count + 1
    EndIf
    exits >> 1
  Next 
  If count = 0
    dir(count) = #dir_Nowhere
    count = 1
  EndIf 
  Redim dir(count - 1)
EndProcedure

Procedure parseItem(item.s)
  Protected i, type = #itm_nothing
  For i = 0 To #itm_count - 1
    If itemName(i) = item
      type = i
      Break
    EndIf
  Next
  ProcedureReturn type
EndProcedure

Procedure greet(*room.room)
  display(#CR$ + "You are in room (" + Str(player\location\x) + ", " +  Str(player\location\y) + ", " + Str(player\location\z) + "), ", #con_White)
  If player\location\x = prizeRoom\x And player\location\y = prizeRoom\y And player\location\z = prizeRoom\z
    display(*room\name, #con_Yellow, #con_Bright_Blue)
    If Not foundPrizeRoom
      display(".  It took you " + Str(hintCount) + " hints to find this room.  I bet your arms are tired!  After you've rested a bit you can start patching up all the holes..", #con_White)
      foundPrizeRoom = #True
    EndIf 
  Else
    display(*room\name, #con_Black, #con_White)
  EndIf 
  displayN(".  It is a " + *room\description + ".", #con_White)
 
  Dim dir_present(0)
  decodeExits(dir_present(), *room\exits)
  Protected i, count = ArraySize(dir_present()) + 1
  
  Select count
    Case 1
      If dir_present(0) = #dir_Nowhere
        displayN("There are no exits from this room. Perhaps you need to make one?", #con_Yellow)
      Else
        displayN("There is an exit " + dirText(dir_present(0))\exitDesc, #con_Yellow)
      EndIf 
    Default 
      display("There are exits ", #con_Yellow)
      For i = 0 To count - 3
        display(dirText(dir_present(i))\exitDesc + ", ", #con_Yellow)
      Next 
      displayN(dirText(dir_present(count - 2))\exitDesc + " and " + dirText(dir_present(count - 1))\exitDesc + ".", #con_Yellow)
  EndSelect
  
  count = ListSize(*room\items())
  If count > 0
    display("There is a ", #con_Yellow)
    FirstElement(*room\items())
    Select count
      Case 1
        display(itemName(*room\items()), #con_Yellow)
      Default 
        For i = 1 To count - 2
          display(itemName(*room\items()) + ", ", #con_Yellow)
          NextElement(*room\items())
        Next
        display(itemName(*room\items()) + " and a ", #con_Yellow)
        NextElement(*room\items())
        display(itemName(*room\items()), #con_Yellow)
    EndSelect
    displayN(" here.", #con_Yellow)
  Else
    displayN("There is nothing useful here you can take.", #con_Yellow)
  EndIf 
EndProcedure

Procedure mainloop()
  player\location\x = 0: player\location\y = 0: player\location\z = 0
  player\curRoomPtr = *locations(Str(player\location\x) + "," + Str(player\location\y) + "," + Str(player\location\z))
  displayN("Welcome to RCRPG, the PureBasic Edition.", #con_Yellow, #con_Bright_Blue)
  displayN("The aim is to find 'The Prize Room', good luck!", #con_Yellow)
  
  Repeat
    greet(player\curRoomPtr)
    displayN(#CR$ + "Try 'help' for help", #con_Bright_Green)
    display(#CR$ + ">")
    Protected cmd.s = Trim(Input())
    While cmd <> "" And quitNow = #False
      Protected command.s = StringField(cmd, 1, " ")
    
      If FindMapElement(*commands(), command)
        Protected function.command_prt = *commands()
        cmd = function(shift(cmd))
      Else
        displayN("'" + StringField(cmd, 1, " ") + "' didn't make any sense.  Try 'help'")
        cmd = ""
      EndIf 
    Wend 
  Until quitNow
EndProcedure

;Return pointer to room at the given coordinates, create it if it doesn't already exist.
Procedure roomcheck(x, y, z)
  Protected roomAddr.s = Str(x) + "," + Str(y) + "," + Str(z)
  If FindMapElement(*locations(), roomAddr)
    ProcedureReturn *locations()
  EndIf 
  
  *locations(roomAddr) = AddElement(rooms())
  With rooms()  
    \location\x = x: \location\y = y: \location\z = z
    \name = "Nameless"
    Protected i, newRoomDescript = Random(#count_roomDescriptions - 1) 
    Restore roomDescription
    For i = 0 To newRoomDescript
      Read.s \description
    Next 
    ;add items
    For i = 1 To Random(2)
      AddElement(\items())
      \items() = Random(#itm_count - 2) + 1
    Next 
  EndWith
  ProcedureReturn *locations()
EndProcedure

Procedure isLadderPresent(List items.i())
  ForEach items()
    If items() = #itm_ladder
      ProcedureReturn #True ;ladder present
    EndIf
  Next
  ProcedureReturn #False ;no ladder present
EndProcedure

;check if there is an exit from currentroom in the given direction (vector)
;and move to it if possible
Procedure move(dir.s)
  If player\curRoomPtr\exits & (1 << (dirIndex(dir) - 1))
    Protected curRoom.coord3D = player\curRoomPtr\location
    Protected VECTOR.coord3D = directions(dir)
    Protected *toRoom.room = roomcheck(curRoom\x + VECTOR\x, curRoom\y + VECTOR\y, curRoom\z + VECTOR\z)
    If dir = "up"
      ;check for ladder in room
      If Not isLadderPresent(player\curRoomPtr\items())
        displayN("There needs to be a ladder in the room before you can climb up.")
        ProcedureReturn
      EndIf 
    ElseIf dir = "down"
      ;check for ladder in lower room
      If Not isLadderPresent(*toRoom\items())
        displayN("There needs to be a ladder in that room before you can climb down.")
        ProcedureReturn
      EndIf 
    EndIf 
    player\curRoomPtr = *toRoom
    player\location = player\curRoomPtr\location
  Else
    displayN("Your way is blocked.")
  EndIf
EndProcedure

Procedure.s cmd_north(cmd.s)
  move("north")
  ProcedureReturn cmd.s
EndProcedure

Procedure.s cmd_south(cmd.s)
  move("south")
  ProcedureReturn cmd.s
EndProcedure

Procedure.s cmd_east(cmd.s)
  move("east")
  ProcedureReturn cmd.s
EndProcedure

Procedure.s cmd_west(cmd.s)
  move("west")
  ProcedureReturn cmd.s
EndProcedure

Procedure.s cmd_up(cmd.s )
  move("up")
  ProcedureReturn cmd.s
EndProcedure

Procedure.s cmd_down(cmd.s)
  move("down")
  ProcedureReturn cmd.s
EndProcedure

;connect two existing rooms via a related exit
Procedure link(*fromRoom.room, direction.s)
  ;calculate coord of toRoom
  Protected toPos.coord3D = directions(direction)
  toPos\x + *fromRoom\location\x:  toPos\y + *fromRoom\location\y :  toPos\z + *fromRoom\location\z 
  
  ;check if toRoom exists, if not create it (roomcheck)
  Protected *toRoom.room = roomcheck(toPos\x, toPos\y, toPos\z)
  
  ;place exit in fromRoom and corresponding exit direction in toRoom
  *fromRoom\exits | (1 << (dirIndex(direction) - 1))
  *toRoom\exits | (1 << (dirIndex(dirText(dirIndex(direction))\reverseName) - 1))
EndProcedure

Procedure.s cmd_attack(cmd.s)
  Protected direction.s = StringField(cmd, 1, " ")

  If direction = "help"
		displayN("Usage: attack (direction)", #con_Bright_Green)
    ProcedureReturn shift(cmd)
  EndIf 

	If direction <> ""
    If Not FindMapElement(directions(), direction)
      display("I don't now that direction.  Try: ")
      Protected output.s
 			ForEach directions()
        output + " " + MapKey(directions()) + ","
      Next
      displayN(Trim(output, ","))
      ProcedureReturn ""
    EndIf 
	Else
		displayN("Invalid syntax.  Try 'attack (direction)'")
    ProcedureReturn ""
  EndIf 
 
	If player\equipped = parseItem("sledge") 
    If dirIndex(direction) = #dir_Nowhere
      displayN("You destroyed a portion of the " + direction + " and the result is nothing.")
    ElseIf player\curRoomPtr\exits & (1 << (dirIndex(direction) - 1))
			displayN("You swing your sledge wildly.")
		Else
			displayN("You bash until the surface crumbles, leaving a hole you can crawl through.")
      link(player\curRoomPtr, direction)
		EndIf
	Else
		displayN("You accomplish nothing.")
	EndIf
  ProcedureReturn shift(cmd)
EndProcedure

Procedure.s cmd_name(cmd.s)
  Protected sep.s = " ", shiftCount = 1
  
  If Left(cmd, 1) = #DQUOTE$
    sep = #DQUOTE$  : shiftCount = 2
  EndIf 
  
  Protected newName.s = StringField(cmd, shiftCount, sep)
  
 	If newName = "help" Or newName = ""
    displayN("Usage: name (New name of room)", #con_Bright_Green)
    ProcedureReturn ""
  EndIf 
  
  Protected *room.room = player\curRoomPtr
  *room\name = newName 
  displayN("Room is now named '" + *room\name + "'.")
  ProcedureReturn shift(cmd, shiftCount, sep)
EndProcedure

Procedure.s cmd_equip(cmd.s)
  Protected toEquip.s = StringField(cmd, 1, " ")
  
	If toEquip = "help" Or toEquip = ""
    displayN("Usage: equip (itemname)", #con_Bright_Green)
    ProcedureReturn shift(cmd)
  EndIf 
  
  ;Find it
  Protected type = parseItem(toEquip)
  If player\equipped = type And type <> #itm_nothing
    displayN("You are already equipped with your " + toEquip + ".")
  ElseIf type = #itm_nothing
    player\equipped = type
    displayN("You are now equipped with " + toEquip + ".")
  Else
    Protected found
    ;Equip it.
    ForEach player\inventory()
      If player\inventory() = type
        found = 1
        player\equipped = type
        Break
      EndIf 
    Next 
    
    If found
      displayN("You equipped your " + toEquip + ".  Put it to good use.")
    Else 
      displayN("You don't have one of those.  Try 'i' to see what you have.")
    EndIf
  EndIf
  
  ProcedureReturn shift(cmd)
EndProcedure

Procedure move_items(List fromRef.i(), List toRef.i(), cmd.s)
  Protected item.s = StringField(cmd, 1, " ")
  Protected moveall, moved, type
  
  If item = "all"
    moveall = #True
    type = #itm_any
  Else 
    type = parseItem(item)
  EndIf 

  LastElement(toRef())
  ForEach fromRef()
    If type = #itm_any  Or fromRef() = type
      AddElement(toRef())
      toRef() = fromRef()
      DeleteElement(fromRef())
      moved + 1
      If Not moveall: Break: EndIf
    EndIf 
  Next 
    
  ProcedureReturn moved
EndProcedure

Procedure.s cmd_take(cmd.s)
  Protected item.s  = StringField(cmd, 1, " ")
  
 	If item = "help" Or Len(cmd) = 0
    displayN("Usage: take {all|(itemname)}", #con_Bright_Green)
    ProcedureReturn ""
  EndIf 

  Protected *room.room = player\curRoomPtr
  Protected moved = move_items(*room\items(), player\inventory(), cmd)
  Select moved
    Case 0
      displayN("There aren't any of those here.")
    Case 1
      displayN("You took 1 item.")
    Default 
      displayN("You took " + Str(moved) + " items.")
  EndSelect 
  ProcedureReturn shift(cmd)
EndProcedure

Procedure.s cmd_drop (cmd.s)
  Protected item.s = StringField(cmd, 1, " ")

	If item = "help" Or item = ""
    displayN("Usage: drop {all|(itemname)}", #con_Bright_Green)
    ProcedureReturn ""
  EndIf 
 
  Protected *room.room = player\curRoomPtr
  Protected moved = move_items(player\inventory(), *room\items(), cmd)
	Select moved
    Case 0
      displayN("You don't have any of those.")
    Case 1
      displayN("You dropped 1 item.")
    Default
      displayN("You dropped " + Str(moved) + " items.")
  EndSelect 
  
  ;Check to see if we dropped our equipped item.
  If moved And player\equipped
    Protected type = parseItem(item)
    Protected droppedEquipped = #True
    If type <> #itm_nothing
      ForEach player\inventory()
        If player\inventory() = player\equipped
          droppedEquipped = #False
          Break
        EndIf 
      Next 
    EndIf
    If droppedEquipped
      displayN("You dropped the item you were using.")
      cmd_equip("nothing")
    EndIf 
  EndIf 
  ProcedureReturn shift(cmd)
EndProcedure

Procedure.s cmd_inventory(cmd.s)
  Protected help.s = StringField(cmd, 1, " ")

	If help = "help"
    displayN("Usage: inventory", #con_Bright_Green)
  	ProcedureReturn ""
  EndIf 
 
  Protected count = ListSize(player\inventory())
	If count
    FirstElement(player\inventory())
    display("You have a ")
    Select count
      Case 1
        display(itemName(player\inventory()))
      Default
        Protected i
        For i = 1 To count - 2
          display(itemName(player\inventory()) + ", ")
          NextElement(player\inventory())
        Next
        display(itemName(player\inventory()) + " and a ")
        NextElement(player\inventory())
        display(itemName(player\inventory()))
    EndSelect
    displayN(".")
  Else
    displayN("You're not carrying anything.  Ask again later.")
  EndIf 
  ProcedureReturn shift(cmd)
EndProcedure

Procedure Max(x, y)
  If x < y
    ProcedureReturn y
  EndIf
  ProcedureReturn x
EndProcedure

Procedure.s cmd_hint(cmd.s)
  If StringField(cmd, 1, " ")
    displayN("Usage: hint", #con_Bright_Green)
    ProcedureReturn ""
  EndIf 
  
  ;check for gold
  Protected hasGold = #False 
  ForEach player\inventory()
    If player\inventory() = #itm_gold
      hasGold = #True
      Break
    EndIf 
  Next 
    
  ;if gold is not present then exit
  If Not hasGold
    displayN("You can't get a hint, you need gold.")
    ProcedureReturn cmd
  EndIf 
  
  display("Will you pay a gold for a hint (y/n)?")
  Protected Answer.s = Input()
  displayN("")
  If LCase(Left(Answer, 1)) <> "y"
    displayN("You must be saving your money for retirement, good luck!")
    ProcedureReturn cmd
  EndIf 
  DeleteElement(player\inventory())
  hintCount + 1
  
  ;calculate vector to prizeroom
  Protected VECTOR.coord3D, maxVector.coord3D, max
  VECTOR\x = prizeRoom\x - player\location\x
  VECTOR\y = prizeRoom\y - player\location\y
  VECTOR\z = prizeRoom\z - player\location\z
  If Abs(VECTOR\y) >= Max(Abs(VECTOR\x), Abs(VECTOR\z)): max = 1: EndIf
  If Abs(VECTOR\z) >= Max(Abs(VECTOR\x), Abs(VECTOR\y)): max = 2: EndIf
  
  Protected *ptrVec.Integer = @VECTOR + max * SizeOf(Integer)
  Protected *ptrMaxVec.Integer = @maxVector + max * SizeOf(Integer)
  If *ptrVec\i < 0
    *ptrMaxVec\i = -1
  ElseIf *ptrVec\i > 0
    *ptrMaxVec\i = 1
  EndIf 
  
  ForEach directions()
    With directions()
      If \x = maxVector\x And \y = maxVector\y And \z = maxVector\z
        Protected dir.s = MapKey(directions())
        Break
      EndIf
    EndWith
  Next 
  display("You place the gold into a small slot on the wall.  A message appears on the wall in bright letters that says '")
  display("Go " + dir, #con_Bright_Blue, #con_Yellow)
  displayN("'.  After a few moments it fades from view.")
   
  ProcedureReturn cmd.s
EndProcedure

Procedure.s cmd_quit(cmd.s)
  Protected help.s = StringField(cmd, 1, " ")
  If help = "help"
    displayN("Usage: quit", #con_Bright_Green)
    ProcedureReturn ""
  EndIf
  
  displayN("Goodbye!")
  quitNow = #True
  ProcedureReturn ""
EndProcedure

Procedure.s cmd_alias(cmd.s)
  Protected existing.s = StringField(cmd, 1, " ")
  Protected newCommand.s = StringField(cmd, 2, " ")
  
  If existing = "help" Or existing = "" Or newCommand = ""
    displayN("Usage: alias (existing command name) (additional name for command)", #con_Bright_Green)
    ProcedureReturn ""
  EndIf
  
	If FindMapElement(*commands(), newCommand)
    displayN( "Can't redefine an existing command!")
    ProcedureReturn ""
  EndIf 
  
  If Not FindMapElement(*commands(), existing)
    displayN("The command '" + existing + "' is not currently defined")
  Else 
    Protected function =  *commands()
  EndIf
  *commands(newCommand) = function
  displayN("alias '" + newCommand + "' defined.")
  ProcedureReturn shift(cmd, 2)
EndProcedure

Procedure.s cmd_help(cmd.s)
  Protected command.s = StringField(cmd, 1, " ")
  If command = "help"
    displayN("Usage: help (command name) ... But you apparently discovered that.", #con_Bright_Green)
    ProcedureReturn ""
  ElseIf FindMapElement(*commands(), command)
    Protected function.command_prt = *commands()
    function("help " + command)
  Else
    displayN("Valid commands are:" + #CR$, #con_Bright_Green)
    ResetMap(*commands())
    Protected i, count = MapSize(*commands())
    For i = count - 1 To 1 Step - 1
      NextMapElement(*commands())
      display(MapKey(*commands()), #con_Bright_Green)
      If i <> 1
        display(", ", #con_Bright_Green)
      EndIf 
    Next 
    displayN(".", #con_Bright_Green)
  EndIf 
  ProcedureReturn ""
EndProcedure

*commands("up") = @cmd_up()
*commands("down") = @cmd_down()
*commands("north") = @cmd_north()
*commands("south") = @cmd_south()
*commands("east") = @cmd_east()
*commands("west") = @cmd_west()
*commands("attack") = @cmd_attack()
*commands("name") = @cmd_name()
*commands("take") = @cmd_take()
*commands("drop") = @cmd_drop()
*commands("equip") = @cmd_equip()
*commands("inventory") = @cmd_inventory()
*commands("ask") = @cmd_hint()
*commands("quit") = @cmd_quit()
*commands("alias") = @cmd_alias()
*commands("help") = @cmd_help()

cmd_alias("north n")
cmd_alias("south s")
cmd_alias("east e")
cmd_alias("west w")
cmd_alias("up u")
cmd_alias("down d")
cmd_alias("attack a")
cmd_alias("inventory i")
cmd_alias("inventory inv")
cmd_alias("quit q")

isConsoleAvailable = OpenConsole()
If isConsoleAvailable
  mainloop()

  display(#CR$ + #CR$ + "Press ENTER to exit"): Input()
  CloseConsole()
EndIf