Elevator and stairs (Hugo example)

From IFWiki

I didn't make this quite as efficient as I'd like, but oh well...

!::
! Elevator and stairs by Roody Yogurt
!::
constant GAME_TITLE	"ELEVATOR ACTION"

!:: Flags

#set VERBSTUBS           ! Include the grammar and routines from verbstubs.g
                          !   and verbstubs.h, respectively

!:: Grammar Library Inclusions (grammar must come first)
! put your new grammar here
verb "take"

* (stairs) "up"/"down"  DoTakeStairs
!	* multi                                                 DoGet


#include "verblib.g"
#include "hugolib.h"     ! Standard Hugo Library

!::	Game Initialization	routine
routine	init
{
	word[1] = ""
	MAX_SCORE = 0   ! ready to change for a scored game
	STATUSTYPE = 1  ! 0 = none :: 1 = score/turns :: 2 = time
	counter = -1    ! 1 step before first turn
	verbosity =	2
!\ VERBOSITY -
! 1	= normal (rooms	described only if not previously described)
! 2	= full (rooms described	every time they	are	entered)
\!

#if	defined	GAME_TITLE
	display.title_caption =	GAME_TITLE	!
! display.title_caption - used for GUI clients for title bar text.
#endif
	prompt = ">"

	window 0 ! resets the windows in case the player is	restarting a game
	cls

	DEFAULT_FONT = PROP_ON
	Font(DEFAULT_FONT)


	Font(BOLD_ON)
	"Elevator Action"
	Font(BOLD_OFF)

	player = you
	location = groundfloor
	old_location = location
	MovePlayer(location)
	CalculateHolding(player)

#ifset USE_PLURAL_OBJECTS
	InitPluralObjects
#endif
}

!::	Main game loop
routine	main
{
	counter	= counter +	1
	run	location.each_turn
	runevents
	RunScripts
	if speaking	not	in location
		speaking = 0
	PrintStatusLine
}

!\ I also used PreParse to allow "go up stairs" and "go stairs" (which I
would redirect to "take stairs" \!

player_character you "you"
{
before
	{
	actor PreParse
		{
		if word[1] = "go","climb","walk"
			{
			if word[2] = "stairs"
				{
				word[1] = "take"
				return true
				}
			elseif (word[2] = "up","down") and word[3] = "stairs"
				{
				DeleteWord(3, 1) ! delete "stairs" and reparse
				return true
				}
			}
		return false
		}
	}
}

!\ For readability, I made some constants to represent the different floors.
GROUND = 1, SECOND = 2, THIRD = 3 \!

enumerate start = 1
{
GROUND, SECOND, THIRD
}

property floor_number alias misc

room groundfloor "Ground Floor"
{
	floor_number GROUND
	long_desc
		{
		"You are on the ground floor. The ";
		OpenOrClosed
		" door to the elevator is north, and stairs lead up."
		}
	u_to {
			if verbroutine = &DoGo
				Trudge
			return secondfloor
			}
	cant_go
		{
		if object = d_obj
			{
			"The stairs only lead up."
			}
		elseif object = n_obj, in_obj
			"The elevator doors are closed."
		else
			return false
		}
	n_to
		{
		if location.floor_number = doors.current_open_door
			{
			if verbroutine = &DoGo
				{
				"You step into the elevator."
				}
			return elevator
			}
		else
			return false
		}
	in_to
		return self.n_to

}


room secondfloor "Second Floor"
{
	floor_number SECOND
	long_desc
		{
		"You are on the second floor. The ";
		OpenOrClosed
		" door to the elevator is north, and stairs go both up and down from
		here."
		}
	u_to {
			if verbroutine = &DoGo
				Trudge
			return thirdfloor
			}
	d_to {
			if verbroutine = &DoGo
				Trudge
			return groundfloor
			}
	cant_go
		{
		if object = n_obj, in_obj
			"The elevator doors are closed."
		else
			return false
		}
	n_to
		{
		if location.floor_number = doors.current_open_door
			{
			if verbroutine = &DoGo
				{
				"You step into the elevator."
				}
			return elevator
			}
		else
			return false
		}
	in_to
		return self.n_to
}

room thirdfloor "Third Floor"
{
	floor_number THIRD
	long_desc
		{
		"You are on the ground floor. The ";
		OpenOrClosed
		" door to the elevator is north, or you can take the stairs
		down."
		}
	d_to {
			if verbroutine = &DoGo
				Trudge
			return secondfloor
			}
	cant_go
		{
		if object = u_obj  ! if player tries to go up
			{
			"The stairs only lead down."
			}
		elseif object = n_obj, in_obj
			"The elevator doors are closed."
		else
			return false
		}
	n_to
		{
		if location.floor_number = doors.current_open_door
			{
			if verbroutine = &DoGo
				{
				"You step into the elevator."
				}
			return elevator
			}
		else
			return false
		}
	in_to
		return self.n_to
}

!\ This routine is called when the player takes the stairs. the "main" routine
is called twice. it'll be called again when the turn is over, making 3 turns
pass to get between floors. I also temporarily clear the location global
so event text isn't printed while the player is moving. \!

routine Trudge
{
	local a
	a = location
	location = 0
	"You trudge ";
	print object.noun #2;
	" the stairs."
	main : main
	location = a
}

routine OpenOrClosed
{
	if location.floor_number = doors.current_open_door or
	(location = elevator and doors.current_open_door)
		"open";
	else
		"closed";
	return
}

scenery stairs "stairs"
{
noun "stairs"
article "the"
found_in groundfloor secondfloor thirdfloor
	long_desc
	{
	select location
		case groundfloor : "They go up."
		case secondfloor : "They go up and down."
		case thirdfloor : "They go down."
	}
before
	{}
}

attribute glowing alias special

scenery callbutton "call button"
{
current_floor 0
noun "button"
adjective "call"
article "the"
found_in groundfloor secondfloor thirdfloor
long_desc
	{
	if self is glowing
		"The call button is lit."
	else
		"The call button is dark."
	}
before
	{
	object DoPush
		{
		if self is glowing
			{
			"The call button has already been pressed."
			}
		elseif location.floor_number ~= elevator.current_floor
			{
			"The call button begins to glow."
			self is glowing
			self.current_floor = location.floor_number
			Activate(moving_elevator)
			}
		elseif location.floor_number = elevator.current_floor and
			not doors.current_open_door
			{
			Activate(door_close,3)
			"The doors slide open."
			doors.current_open_door = location.floor_number
			}
		elseif location.floor_number = doors.current_open_door
			"Nothing happens."
		}
	}
}


daemon moving_elevator
{}

! moving elevator event
event in moving_elevator
{
if doors.current_open_door
	{
	if location = elevator or (elevator.current_floor = location.floor_number)
		{
		Deactivate(door_close)
		"\nThe doors slide closed."
		}
	doors.current_open_door = 0 ! clear it
	return ! takes up one turn
	}
elseif elevator.current_floor ~= callbutton.current_floor
	{
	if location = elevator
		{
		"\nYou feel the elevator going ";
		if callbutton.current_floor > elevator.current_floor
			{
			"up";
			if callbutton.current_floor - 1 = elevator.current_floor
				{
				" (and then come to a stop)";
				}
			"."
			}
		else
			{
			"down";
			if callbutton.current_floor + 1 = elevator.current_floor
				{
				" (and then come to a stop)";
				}
			"."
			}
		}
	elseif location
		"\nYou hear the hum of the elevator moving."
	if callbutton.current_floor > elevator.current_floor
		elevator.current_floor++ ! go up
	else
		elevator.current_floor-- ! go down
	return
	}
elseif elevator.current_floor = callbutton.current_floor
	{
	if location = elevator or location.floor_number = elevator.current_floor
		{
		""
		"The elevator door opens.";
		Activate(door_close,3)
		if location ~= elevator
			" The call button blinks off."
		else
			""
		event_flag = 2
		}
	doors.current_open_door = elevator.current_floor
	callbutton.current_floor = 0
	callbutton is not glowing
	Deactivate(self)
	}
}

fuse door_close
{}

event in door_close ! the "in" is optional
{
	if not doors.current_open_door
		{
		Deactivate(self)
		}
! any code that executes each turn the fuse is running
      if not self.tick
      {
      if location.misc = doors.current_open_door
			{
			"\nThe elevator doors slide closed."
			}
		doors.current_open_door = 0
      }
}

property current_open_door alias misc

scenery doors "elevator doors"
{
noun "doors" "door"
adjective "elevator"
article "the"
is openable
current_open_door 0
found_in groundfloor secondfloor thirdfloor elevator
	long_desc
	{
	"The elevator door is ";
	OpenOrClosed
	if location = elevator
		", and there's a panel of buttons next to it."
	else
		", and there's a call button beside it."
	}
before
	{
	object DoOpen
		{
		if self.current_open_door = location.floor_number or
			(location = elevator and self.current_open_door)
			{
			"The elevator door is already open."
			}
		elseif location = elevator
			"Try pushing a floor number."
		else
			"Try pushing the call button."
		}
	object DoClose
		{
		if self.current_open_door = location.floor_number or
			(location = elevator and self.current_open_door)
			{
			"The elevator door will close automatically when it needs to."
			}
		else
			"The elevator door is already closed."
		}
	}
}

property current_floor alias misc

room elevator "Elevator"
{
	current_floor GROUND ! starts on the ground floor
	long_desc
		{
		"You are inside the elevator. The door to the south is ";
		OpenOrClosed
		", and there's the usual panel of buttons beside it."
		}
	s_to
		{
		if self.current_floor = doors.current_open_door
			{
			if verbroutine = &DoGo
				{
				"You step out of the elevator."
				}
			select doors.current_open_door
				case GROUND : return groundfloor
				case SECOND : return secondfloor
				case THIRD : return thirdfloor
			}
		else
			return false
		}
	out_to
		return self.s_to
	cant_go
		{
		if object = s_obj
			"The elevator doors are closed."
		else
			return false
		}
}

scenery panel "panel of buttons"
{
noun "buttons" "panel"
adjective "panel" "of"
article "the"
in elevator
long_desc
	{
	"The panel has three buttons: G, 2, and 3. There is also a LED display that reads ";
	SelectFloor
	""
	}
before
	{
	object DoPush
		{
		"You can only push one button at a time."
		}
	}
}

!\ I made the buttons and display components so trying to get them
would get the "that is part of the panel of buttons" response \!

component LED_display "LED display"
{
adjective "led"
noun "display"
article "the"
part_of panel
long_desc
	{
	"The LED display is showing a large red ";
	SelectFloor
	""
	}
}

component g_button "G button"
{
	article "the"
	adjective "g"
    noun "button"
    part_of panel
long_desc
	{
	"The G button is ";
	LitorDark(self)
	}
before
	{
	object DoPush
		{
		ElevatorPush(self)
		}
	}
}

component 2_button "2 button"
{
long_desc
	{
	"The 2 button is ";
	LitorDark(self)
	}
article "the"
adjective "2"
noun "button"
part_of panel
before
	{
	object DoPush
		{
		ElevatorPush(self)
		}
	}
}

component 3_button "3 button"
{
long_desc
	{
	"The 3 button is ";
	LitorDark(self)
	}
article "the"
adjective "3"
noun "button"
part_of panel
before
	{
	object DoPush
		{
		ElevatorPush(self)
		}
	}
}

routine LitorDark(obj)
{
local a
select obj
	case g_button : a = GROUND
	case 2_button : a = SECOND
	case 3_button : a = THIRD
if a = callbutton.current_floor
	"lit."
else
	"dark."
}

routine SelectFloor
{
	"\"";
	select elevator.current_floor
		case GROUND : "G.\"";
		case SECOND : "2.\"";
		case THIRD : "3.\"";
}

routine ElevatorPush(obj)
{

local a
select obj
	case g_button : a = GROUND
	case 2_button : a = SECOND
	case 3_button : a = THIRD
if callbutton.current_floor = a
	{
	print CThe(obj) ; " is already lit."
	return
	}

if a = elevator.current_floor
	{
	if doors.current_open_door = a ! doors are already open
		{
		"Nothing happens."
		Activate(door_close, 3) ! extend door-open length by two turns
		return
		}
	else
		{
		if callbutton.current_floor and
		callbutton.current_floor ~= a
			{
			callbutton.current_floor = 0
			"The elevator abruptly stops, and the doors slide open."
			doors.current_open_door = a
			Deactivate(moving_elevator)
			return
			}
		else
			{
			Deactivate(moving_elevator)
			"The doors slide open again."
			doors.current_open_door = a
			Activate(door_close,3)
			return
			}
		}
	}
else
	{
	print "You push "; The(obj) ;"."
	}
Deactivate(moving_elevator)
callbutton.current_floor = a
Activate(moving_elevator)
}
routine DoTakeStairs
{
if word[3] ~= "up", "down"
	{
	select location.floor_number
		case 1 : word[3] = "up"
		case 3 : word[3] = "down"
	}
select word[3]
	case "up" : return Perform(&DoGo,u_obj)
	case "down" : return Perform(&DoGo,d_obj)
	case else
		{
		"Do you want to take the stairs up, or take the stairs down?"
		return false ! Don't take up a turn
		}

}

!\ If the player tries "take stairs" from the second floor, I didn't want the
"which stairs did you mean?" response to take up a turn. The easiest way to do
this was to replace DoGet and add my own code. If I didn't care about taking a
turn, I could have easily handled it with a before routine in the stairs object.
\!
replace DoGet
{
	local b, p
	if object = stairs and word[1] = "take"
		{
		select location
			case groundfloor : return Perform(&DoGo, u_obj)
			case secondfloor
				{
				"Do you want to take the stairs up, or take the stairs down?"
				return false ! Don't take up a turn
				}
			case thirdfloor : return Perform(&DoGo, d_obj)
		}
	if object in player
		VMessage(&DoGet, 1)      ! "You already have that."
	elseif object = player
	{
		VMessage(&DoGet, 2)     ! player trying to get player
		return false
	}
	elseif object is living and object is static
	{
		VMessage(&DoGet, 3)     ! player trying to get character
		return false
	}
	elseif parent(object) is living and parent(object) is unfriendly
		VMessage(&DoGet, 4)      ! "X doesn't want to give it to you."
	elseif (parent(object) is openable, not open) and
		parent(object) is container:
	{
		VMessage(&DoGet, 5)     ! "X is closed."
		return false
	}
	elseif Contains(object, player)
	{
		if object is static
			VMessage(&DoGet, 7)	! "You can't take that."
		else
			VMessage(&DoGet, 6)     ! "Not while you're in/on it..."
		return false
	}
	else
	{
		if not CheckReach(object)
			return false
		elseif object is static
		{
			VMessage(&DoGet, 7)      ! "You can't take that."
			return true
		}

		! Because the engine calls location.before
		if (parent(object)~=location)
			b = parent(object).before

		if not b
		{
			xobject = parent(object)

			if object not in location
			{
				CalculateHolding(xobject)
				p = xobject
			}

			if Acquire(player, object)
			{
				object is not hidden

				if not object.after
				{
					! Again, to avoid duplication or
					! echoing:
					!
					b = 0
					if xobject ~= location
						b = xobject.after

					if b = false
						! "Taken."
						VMessage(&DoGet, 8)
				}
			}
			else
				! "You're carrying too much to take that."
				VMessage(&DoGet, 9)
		}
	}
	return true
}