#charset "us-ascii"
/*
* Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved.
*
* TADS 3 Library - Status Line
*
* This module defines the framework for displaying the status line,
* which is the area conventionally displayed at the top of the screen
* showing information such as the current location, score (if scoring is
* used at all), and number of turns.
*/
/* include the library header */
#include "advlite.h"
/* ------------------------------------------------------------------------ */
/*
* In case the 'score' module isn't included, make sure we can refer to
* totalScore as a property. Likewise for the banner API and Web UI
* frame API.
*/
property totalScore;
property showBanner, setSize, sizeToContents;
property flushWin, resize;
/* ------------------------------------------------------------------------ */
/*
* A special OutputStream for the <BANNER> tag contents. This is really
* just part of the main output stream, but we use a separate output
* stream object so that we have our own separate stream state variables
* (for paragraph breaking and so forth).
*/
transient statusTagOutputStream: OutputStream
/*
* We're really part of the main window's output stream as far as the
* underlying interpreter I/O system is concerned, so we have to
* coordinate with the main game window's input manager.
*/
myInputManager = inputManager
/* we sit atop the system-level main console output stream */
writeFromStream(txt)
{
/* write the text directly to the main output stream */
tadsSay(txt);
}
;
/*
* A special OutputStream for the left half of the status line (the
* short description area) in text mode. We use a separate stream for
* this because we must write the text using the output mode switching
* for the status line.
*
* We only use this stream when we use the old-style text-mode status
* line interface, which explicitly separates the status line into a
* left part and a right part. When we have the banner API available in
* the interpreter, we'll use banners instead, since banners give us
* much more flexibility.
*/
transient statusLeftOutputStream: OutputStream
/* we sit atop the system-level main console output stream */
writeFromStream(txt)
{
/* write the text directly to the main output stream */
tadsSay(txt);
}
;
/*
* A special OutputStream for the right half of the status line (the
* score/turn count area) in text mode. We use a separate stream for
* this because we have to write this text with the special
* statusRight() intrinsic in text mode.
*
* We only use this stream when we use the old-style text-mode status
* line interface, which explicitly separates the status line into a
* left part and a right part. When we have the banner API available in
* the interpreter, we'll use banners instead, since banners give us
* much more flexibility.
*/
transient statusRightOutputStream: OutputStream
/*
* Write from the stream. We simply buffer up text until we're
* asked to display the final data.
*/
writeFromStream(txt)
{
/* buffer the text */
buf_ += txt;
}
/*
* Flush the buffer. This writes whatever we've buffered up to the
* right half of the text-mode status line.
*/
flushStream()
{
/* write the text to the system console */
statusRight(buf_);
/* we no longer have anything buffered */
buf_ = '';
}
/* our buffered text */
buf_ = ''
;
/* ------------------------------------------------------------------------ */
/*
* Statusline modes. We have three different ways to display the
* statusline, depending on the level of support in the interpreter.
*
* StatusModeApi - use the banner API. This is preferred method, because
* it gives us uniform capabilities on text and graphical interpreters,
* and provides an output stream for the statusline that is fully
* independent on the main game window's output stream.
*
* StatusModeTag - use the <BANNER> tag. This is the method we must use
* for HTML-enabled interpreters that don't support the banner API. This
* gives us the full formatting capabilities of HTML, but isn't as good
* as StatusModeApi because we have to share our output stream with the
* main game window.
*
* StatusModeText - use the old-style dedicated statusline in a text-only
* interpreter. This is the least desirable method, because it gives us
* a rigid format for the statusline (exactly one line high, no control
* over colors, and with the strict left/right bifurcation). We'll only
* use this method if we're on a text-only interpreter that doesn't
* support the banner API.
*
* StatusModeBrowser - use the Web UI. This is similar to API mode, but
* instead of using banner windows we use Web UI windows, which are
* implemented as IFRAMEs in the browser.
*/
enum StatusModeApi, StatusModeTag, StatusModeText, StatusModeBrowser;
/* ------------------------------------------------------------------------ */
/*
* Status line - this is an abstract object that controls the status line
* display.
*
* We provide two main methods: showStatusHtml, which shows the status
* line in HTML format, and showStatusText, which shows the status line
* in plain text mode. To display the status line, we invoke one or the
* other of these methods, according to the current mode, to display the
* statusline. The default implementations of these methods generate the
* appropriate formatting codes for a statusline with a left part and a
* right part, calling showStatusLeft and showStatusRight, respectively,
* to display the text for the parts.
*
* Games can customize the statusline at two levels. At the simpler
* level, a game can modify showStatusLeft and/or showStatusRight to
* change the text displayed on the left and/or right of the statusline.
* Since these two methods are used regardless of the statusline style of
* the underlying interpreter, games don't have to worry about the
* different modes when overriding these.
*
* At the more complex level, a game can modify showStatusHtml and/or
* showStatusText. Modifying these routines provides complete control
* over the formatting of the entir status line. If a game wants to use
* something other than the traditional left/right display, it must
* modify these methods.
*
* This object is transient, because the statusline style is a function
* of the interpreter we're currently running on, and thus isn't suitable
* for saving persistently.
*/
transient statusLine: object
/*
* Show the status line, in HTML or text mode, as appropriate. By
* default, the library sets this up as a "prompt daemon," which
* means that this will be called automatically just before each
* command line is read.
*/
showStatusLine()
{
local oldStr;
/* if the status line isn't active, or there's no PC, skip this */
if (statusDispMode == nil || gPlayerChar == nil)
return;
/*
* Enter status-line mode. This will do whatever is required for
* our current status-line display style to prepare the output
* manager so that any text we display to the default output
* stream is displayed on the status line.
*/
oldStr = beginStatusLine();
/* make sure we restore statusline mode before we're done */
try
{
/*
* Generate a text or HTML status line, as appropriate. If
* we're in <BANNER> tag mode or banner API mode, use HTML to
* format the contents of the status line; if we're using the
* old-style text mode, use plain text, since the formatting
* is rigidly defined in this mode.
*/
switch (statusDispMode)
{
case StatusModeTag:
case StatusModeApi:
case StatusModeBrowser:
/* show the HTML status line */
showStatusHtml();
break;
case StatusModeText:
/* show the status line in plain text mode */
showStatusText();
break;
}
}
finally
{
/* end status-line mode */
endStatusLine(oldStr);
}
}
/* prompt-daemon showing the status line */
showStatusLineDaemon()
{
/* show the status line as normal */
showStatusLine();
/*
* Explicitly flush the status line if it's in a banner window.
* This will ensure that we'll redraw the status line on each
* turn if we're reading an input script, which is nice because
* it provides a visual indication that something's happening.
*/
if (statusDispMode is in (StatusModeApi, StatusModeBrowser))
statuslineBanner.flushBanner();
}
/*
* Show the status line in HTML format. Our default implementation
* shows the traditional two-part (left/right) status line, using
* showStatusLeft() and showStatusRight() to display the parts.
*/
showStatusHtml()
{
/*
* start the left half, and write the <A HREF> to hyperlink the
* location name to a "look around" command
*/
"<<statusHTML(0)>><<
aHref(BMsg(command look around, 'look around'),
nil, nil, AHREF_Plain)>>";
/* show the left part of the status line */
showStatusLeft();
/*
* end the left portion and start the right portion, then
* generate the <A HREF> to link the score to a FULL SCORE
* command
*/
"<./a></a><<statusHTML(1)>><<
aHref(BMsg(command full score, 'full score'), nil, nil, AHREF_Plain)>>";
/* show the right part of the status line */
showStatusRight();
/* end the score link, and end the right half wrapper */
"<./a></a><<statusHTML(2)>>";
/* add the status-line exit list, if desired */
if (gPlayerChar.location!= nil)
gPlayerChar.location.showStatuslineExits();
}
/*
* Get the estimated HTML-style banner height, in lines of text.
* This is used to set the status line banner size for platforms
* where sizing to the exact height of the rendered contents isn't
* supported.
*
* If showStatusHtml() is overridden to display more or fewer lines
* of text than the basic implementation here, then this routine must
* be overridden as well to reflect the new height.
*/
getEstimatedHeightHtml()
{
local ht;
/*
* we need one line for the basic display (the location name and
* score/turn count)
*/
ht = 1;
/* add in the estimated height of the exits display, if appropriate */
if (gPlayerChar.location != nil)
ht += gPlayerChar.location.getStatuslineExitsHeight();
/* return the result */
return ht;
}
/*
* Show the statusline in text mode. Our default implementation
* shows the traditional two-part (left/right) status line, using
* showStatusLeft() and showStatusRight() to display the parts.
*/
showStatusText()
{
/* show the left part of the display */
showStatusLeft();
/* switch to the right-side status stream */
outputManager.setOutputStream(statusRightOutputStream);
/* show the right-half text */
showStatusRight();
/* flush the right-side stream */
statusRightOutputStream.flushStream();
}
/*
* Show the left part of a standard left/right statusline. By
* default, we'll show the player character's location, by calling
* statusName() on the PC's immediate container.
*/
showStatusLeft()
{
local actor;
/* get the player character actor */
actor = gPlayerChar;
"<.statusroom>";
/* show the actor's location's status name */
if (actor != nil && actor.location != nil)
actor.location.statusName(actor);
"<./statusroom>";
}
/*
* Show the right part of a standard left/right statusline. By
* default, we'll show the current score, a slash, and the number of
* turns.
*/
showStatusRight()
{
local s;
/* if there's a score object, show the score */
if ((s = libGlobal.scoreObj) != nil)
{
/* show the score and the number of turns so far */
"<.statusscore><<s.totalScore>>/<<
libGlobal.totalTurns>><./statusscore>";
}
/* Just show the number of turns */
else
"<.statusscore><<
libGlobal.totalTurns>><./statusscore>";
}
/*
* Set up the status line's color scheme. This is called each time
* we redraw the status line to set the background and text colors.
* We call the statusline banner window to do the work, since the
* mechanism is different between the traditional and Web UIs.
*/
setColorScheme()
{
/* call the banner window to do the work */
statuslineBanner.setColorScheme();
}
/*
* Begin status-line mode. This sets up the output manager so that
* text written to the default output stream is displayed on the
* status line. Returns the original output stream.
*/
beginStatusLine()
{
local oldStr;
/* check what kind of statusline display we're using */
switch(statusDispMode)
{
case StatusModeApi:
/*
* We have a banner API window. Start by clearing the
* window, so we can completely replace everything in it.
*/
statuslineBanner.clearWindow();
/*
* If the platform doesn't support size-to-contents, then set
* the height to our best estimate for the size.
*
* If we do support size-to-contents, we'll set the height to
* the exact rendered size when we're done, so we don't need
* to worry about setting an estimate; indicate this to the
* interpreter by setting the is-advisory flag to true.
*/
statuslineBanner.setSize(getEstimatedHeightHtml(),
BannerSizeAbsolute, true);
/* switch to the banner's output stream */
oldStr = statuslineBanner.setOutputStream();
/* set up the statusline color in the window */
setColorScheme();
/* done */
break;
case StatusModeBrowser:
/* browser UI - clear the window */
statuslineBanner.clearWindow();
/* switch to its output stream */
oldStr = statuslineBanner.setOutputStream();
break;
case StatusModeTag:
/*
* We're using <BANNER> tags. Switch to our statusline
* output stream.
*/
oldStr = outputManager.setOutputStream(statusTagOutputStream);
/* set up the <BANNER> tag */
"<banner id=StatusLine height=previous border>";
/* set up the color scheme */
setColorScheme();
/* done */
break;
case StatusModeText:
/* flush the main window */
flushOutput();
/* plain text mode - enter text status mode */
statusMode(StatModeStatus);
/* switch to the status-left output stream */
oldStr = outputManager.setOutputStream(statusLeftOutputStream);
/* done */
break;
}
/* return the original output stream */
return oldStr;
}
/* end statusline display */
endStatusLine(oldStr)
{
/* restore the old default output stream */
outputManager.setOutputStream(oldStr);
/* check the type of statusline we're generating */
switch (statusDispMode)
{
case StatusModeApi:
/* banner API mode - end the last line */
statuslineBanner.writeToBanner('\n');
/*
* Size the window to its current contents. This doesn't
* work everywhere - on a few platforms, it does nothing -
* but this will give us the optimal size where it's
* supported. On platforms that don't support this, it'll do
* nothing, which means we'll simply be left with the
* "advisory" size we established earlier.
*/
statuslineBanner.sizeToContents();
break;
case StatusModeBrowser:
/* browser mode - flush the window and update the content size */
statuslineBanner.flushWin();
break;
case StatusModeTag:
/* HTML <BANNER> mode - end the <BANNER> tag */
statusTagOutputStream.writeToStream('</banner>');
break;
case StatusModeText:
/* plain text statusline - end status mode */
statusMode(StatModeNormal);
}
}
/*
* Initialize the banner window, given the BannerWindow object
* representing the status line banner API window.
*/
initBannerWindow(win)
{
/*
* Try showing the banner API window. If that succeeds, use the
* banner API window, since it's the most portable and flexible
* way to show the status line. If we can't create the banner
* API window, it means we're on an interpreter that doesn't
* support the banner API, so fall back on one of the older, less
* flexible mechanisms; which older mechanism we choose depends
* on what kind of interpreter we're on.
*
* Since we create the status line banner during initialization
* and normally leave it as the first item in the display list at
* all times, we can attach to an existing status line banner
* window if there is one. This will avoid unnecessary redrawing
* on RESTART.
*/
if (win.showBanner(nil, BannerFirst, nil, BannerTypeText,
BannerAlignTop, nil, nil,
BannerStyleBorder | BannerStyleTabAlign))
{
/*
* we successfully created the banner window - use the banner
* API to show the status line
*/
statusDispMode = StatusModeApi;
}
else if (outputManager.htmlMode)
{
/*
* We failed to create a banner API window, and we're running
* on a full HTML interpreter, so use <BANNER> tags to
* produce the status line.
*/
statusDispMode = StatusModeTag;
}
else
{
/*
* We failed to create a banner API window, and we're running
* on a text-only interpreter - use the old-style
* fixed-format status line mechanism.
*/
statusDispMode = StatusModeText;
}
}
/*
* The status mode we're using. If this is nil, it means we haven't
* chosen a mode yet.
*/
statusDispMode = nil
;
Adv3Lite Library Reference Manual
Generated on 03/07/2024 from adv3Lite version 2.1