exits.t
#charset "us-ascii"
/*
* Copyright (c) 2002, 2006 by Michael J. Roberts
*
* Based on exitslister.t, copyright 2002 by Steve Breslin and
* incorporated by permission.
*
* TADS 3 Library - Exits Lister
*
* This module provides an automatic exit lister that shows the apparent
* exits from the player character's location. The automatic exit lister
* can optionally provide these main features:
*
* - An "exits" verb lets the player explicitly show the list of apparent
* exits, along with the name of the room to which each exit connects.
*
* - Exits can be shown automatically as part of the room description.
* This extra information can be controlled by the player through the
* "exits on" and "exits off" command.
*
* - Exits can be shown automatically when an actor tries to go in a
* direction where no exit exists, as a helpful reminder of which
* directions are valid.
*/
/* include the library header */
#include "adv3.h"
/* ------------------------------------------------------------------------ */
/*
* The main exits lister.
*/
exitLister: PreinitObject
/* preinitialization */
execute()
{
/* install myself as the global exit lister object */
gExitLister = self;
}
/*
* Flag: use "verbose" listing style for exit lists in room
* descriptions. When this is set to true, we'll show a
* sentence-style list of exits ("Obvious exits lead east to the
* living room, south, and up."). When this is set to nil, we'll use
* a terse style, enclosing the message in the default system
* message's brackets ("[Obvious exits: East, West]").
*
* Verbose-style room descriptions tend to fit well with a room
* description's prose, but at the expense of looking redundant with
* the exit list that's usually built into each room's custom
* descriptive text to begin with. Some authors prefer the terse
* style precisely because it doesn't look like more prose
* description, but looks like a separate bit of information being
* offered.
*
* This is an author-configured setting; the library does not provide
* a command to let the player control this setting.
*/
roomDescVerbose = nil
/*
* Flag: show automatic exit listings on attempts to move in
* directions that don't allow travel. Enable this by default,
* since most players appreciate having the exit list called out
* separately from the room description (where any mention of exits
* might be buried in lots of other text) in place of an unspecific
* "you can't go that way".
*
* This is an author-configured setting; the library does not provide
* a command to let the player control this setting.
*/
enableReminder = true
/*
* Flag: enable the automatic exit reminder even when the room
* description exit listing is enabled. When this is nil, we will
* NOT show a reminder with "can't go that way" messages when the
* room description exit list is enabled - this is the default,
* because it can be a little much to have the list of exits shown so
* frequently. Some authors might prefer to show the reminder
* unconditionally, though, so this option is offered.
*
* This is an author-configured setting; the library does not provide
* a command to let the player control this setting.
*/
enableReminderAlways = nil
/*
* Flag: use hyperlinks in the directions mentioned in room
* description exit lists, so that players can click on the direction
* name in the listing to enter the direction command.
*/
enableHyperlinks = true
/* flag: we've explained how the exits on/off command works */
exitsOnOffExplained = nil
/*
* Determine if the "reminder" is enabled. The reminder is the list
* of exits we show along with a "can't go that way" message, to
* reminder the player of the valid exits when an invalid one is
* attempted.
*/
isReminderEnabled()
{
/*
* The reminder is enabled if enableReminderAlways is true, OR if
* enableReminder is true AND exitsMode.inRoomDesc is nil.
*/
return (enableReminderAlways
|| (enableReminder && !exitsMode.inRoomDesc));
}
/*
* Get the exit lister we use for room descriptions.
*/
getRoomDescLister()
{
/* use the verbose or terse lister, according to the configuration */
return roomDescVerbose
? lookAroundExitLister
: lookAroundTerseExitLister;
}
/* perform the "exits" command to show exits on explicit request */
showExitsCommand()
{
/* show exits for the current actor */
showExits(gActor);
/*
* if we haven't explained how to turn exit listing on and off,
* do so now
*/
if (!exitsOnOffExplained)
{
gLibMessages.explainExitsOnOff;
exitsOnOffExplained = true;
}
}
/*
* Perform an EXITS ON/OFF/STATUS/LOOK command. 'stat' indicates
* whether we're turning on (true) or off (nil) the statusline exit
* listing; 'look' indicates whether we're turning the room
* description listing on or off.
*/
exitsOnOffCommand(stat, look)
{
/* set the new status */
exitsMode.inStatusLine = stat;
exitsMode.inRoomDesc = look;
/* confirm the new status */
gLibMessages.exitsOnOffOkay(stat, look);
/*
* If we haven't already explained how the EXITS ON/OFF command
* works, don't bother explaining it now, since they obviously
* know how it works if they've actually used it.
*/
exitsOnOffExplained = true;
}
/* show the list of exits from an actor's current location */
showExits(actor)
{
/* show exits from the actor's location */
showExitsFrom(actor, actor.location);
}
/* show an exit list display in the status line, if desired */
showStatuslineExits()
{
/* if statusline exit displays are enabled, show the exit list */
if (exitsMode.inStatusLine)
showExitsWithLister(gPlayerChar, gPlayerChar.location,
statuslineExitLister,
gPlayerChar.location
.wouldBeLitFor(gPlayerChar));
}
/*
* Calculate the contribution of the exits list to the height of the
* status line, in lines of text. If we're not configured to display
* the exits list in the status line, then the contribution is zero;
* otherwise, we'll estimate how much space we need to display the
* exit list.
*/
getStatuslineExitsHeight()
{
/*
* if we're enabled, our standard display takes up one line; if
* we're disabled, we don't contribute anything to the status
* line's vertical extent
*/
if (exitsMode.inStatusLine)
return 1;
else
return 0;
}
/* show exits as part of a room description */
lookAroundShowExits(actor, loc, illum)
{
/* if room exit displays are enabled, show the exits */
if (exitsMode.inRoomDesc)
showExitsWithLister(actor, loc, getRoomDescLister, illum);
}
/* show exits as part of a "cannot go that way" error */
cannotGoShowExits(actor, loc)
{
/* if we want to show the reminder, show it */
if (isReminderEnabled())
showExitsWithLister(actor, loc, explicitExitLister,
loc.wouldBeLitFor(actor));
}
/* show the list of exits from a given location for a given actor */
showExitsFrom(actor, loc)
{
/* show exits with our standard lister */
showExitsWithLister(actor, loc, explicitExitLister,
loc.wouldBeLitFor(actor));
}
/*
* Show the list of exits using a specific lister.
*
* 'actor' is the actor for whom the display is being generated.
* 'loc' is the location whose exit list is to be shown; this need
* not be the same as the actor's current location. 'lister' is the
* Lister object that will show the list of DestInfo objects that we
* create to represent the exit list.
*
* 'locIsLit' indicates whether or not the ambient illumination, for
* the actor's visual senses, is sufficient that the actor would be
* able to see if the actor were in the new location. We take this
* as a parameter so that we don't have to re-compute the
* information if the caller has already computed it for other
* reasons (such as showing a room description). If the caller
* hasn't otherwise computed the value, it can be easily computed as
* loc.wouldBeLitFor(actor).
*/
showExitsWithLister(actor, loc, lister, locIsLit)
{
local destList;
local showDest;
local options;
/*
* Ask the lister if it shows the destination names. We need to
* know because we want to consolidate exits that go to the same
* place if and only if we're going to show the destination in
* the listing; if we're not showing the destination, there's no
* reason to consolidate.
*/
showDest = lister.listerShowsDest;
/* we have no option flags for the lister yet */
options = 0;
/* run through all of the directions used in the game */
destList = new Vector(Direction.allDirections.length());
foreach (local dir in Direction.allDirections)
{
local conn;
/*
* If the actor's location has a connector in this
* direction, and the connector is apparent, add it to the
* list.
*
* If the actor is in the dark, we can only see the
* connector if the connector is visible in the dark. If
* the actor isn't in the dark, we can show all of the
* connectors.
*/
if ((conn = loc.getTravelConnector(dir, actor)) != nil
&& conn.isConnectorApparent(loc, actor)
&& conn.isConnectorListed
&& (locIsLit || conn.isConnectorVisibleInDark(loc, actor)))
{
local dest;
local destName = nil;
local destIsBack;
/*
* We have an apparent connection in this direction, so
* add it to our list. First, check to see if we know
* the destination.
*/
dest = conn.getApparentDestination(loc, actor);
/* note if this is the "back to" connector for the actor */
destIsBack = (conn == actor.lastTravelBack);
/*
* If we know the destination, and they want to include
* destination names where possible, get the name. If
* there's a name to show, include the name.
*/
if (dest != nil
&& showDest
&& (destName = dest.getDestName(actor, loc)) != nil)
{
local orig;
/*
* we are going to show a destination name for this
* item, so set the special option flag to let the
* lister know that this is the case
*/
options |= ExitLister.hasDestNameFlag;
/*
* if this is the back-to connector, note that we
* know the name of the back-to location
*/
if (destIsBack)
options |= ExitLister.hasBackNameFlag;
/*
* If this destination name already appears in the
* list, don't include this one in the list.
* Instead, add this direction to the 'others' list
* for the existing entry, so that we will show each
* known destination only once.
*/
orig = destList.valWhich({x: x.dest_ == dest});
if (orig != nil)
{
/*
* this same destination name is already present
* - add this direction to the existing entry's
* list of other directions going to the same
* place
*/
orig.others_ += dir;
/*
* if this is the back-to connector, note it in
* the original destination item
*/
if (destIsBack)
orig.destIsBack_ = true;
/*
* don't add this direction to the main list,
* since we don't want to list the destination
* redundantly
*/
continue;
}
}
/* add it to our list */
destList.append(new DestInfo(dir, dest, destName,
destIsBack));
}
}
/* show the list */
lister.showListAll(destList.toList(), options, 0);
}
;
/*
* A destination tracker. This keeps track of a direction and the
* apparent destination in that direction.
*/
class DestInfo: object
construct(dir, dest, destName, destIsBack)
{
/* remember the direction, destination, and destination name */
dir_ = dir;
dest_ = dest;
destName_ = destName;
destIsBack_ = destIsBack;
}
/* the direction of travel */
dir_ = nil
/* the destination room object */
dest_ = nil
/* the name of the destination */
destName_ = nil
/* flag: this is the "back to" destination */
destIsBack_ = nil
/* list of other directions that go to our same destination */
others_ = []
;
/*
* Settings item - show defaults in status line
*/
exitsMode: SettingsItem
/* our ID */
settingID = 'adv3.exits'
/* show our description */
settingDesc =
(gLibMessages.currentExitsSettings(inStatusLine, inRoomDesc))
/* convert to text */
settingToText()
{
/* just return the two binary variables */
return (inStatusLine ? 'on' : 'off')
+ ','
+ (inRoomDesc ? 'on' : 'off');
}
settingFromText(str)
{
/* parse out our format */
if (rexMatch('<space>*(<alpha>+)<space>*,<space>*(<alpha>+)',
str.toLower()) != nil)
{
/* pull out the two variables from the regexp groups */
inStatusLine = (rexGroup(1) [3] == 'on');
inRoomDesc = (rexGroup(2) [3] == 'on');
}
}
/*
* Our value is in two parts. inStatusLine controls whether or not
* we show the exit list in the status line; inRoomDesc controls the
* exit listing in room descriptions.
*/
inStatusLine = true
inRoomDesc = nil
;
TADS 3 Library Manual
Generated on 5/16/2013 from TADS version 3.1.3