pov.t
#charset "us-ascii"
/*
* Copyright (c) 2000, 2006 Michael J. Roberts. All Rights Reserved.
*
* TADS 3 Library - point of view
*
* This module provides definitions related to point of view and sensory
* context. When we generate output, we do so with respect to a
* particular point of view; different points of view can result in
* different output, because of the viewer's distance from an object, for
* example, or because of the presence of obscuring materials between the
* viewer and the viewed object. We also generate output in a particular
* sensory context, which controls whether or not a message that
* describes an object with respect to a particular sense should be
* generated at all; for example, if the viewer can't see an object
* because of darkness or an obscuring layer of material, messages about
* the object's visual appearance should not be generated.
*/
/* include the library header */
#include "adv3.h"
/* ------------------------------------------------------------------------ */
/*
* Call a function with a given sensory context.
*
* The sensory context specifies the source of any messages generated in
* the course of the routine we invoke and the sense which those
* messages use to convey information. If the player character cannot
* sense the source object in the given sense, then we block all
* messages generated while calling this function.
*
* If the source object is nil, this establishes a neutral sense context
* in which all messages are visible.
*
* This can be used for processing events that are not directly
* initiated by the player character, such as non-player character
* activities or scheduled events (fuses and daemons). The idea is that
* anything described in the course of calling our routine is physically
* associated with the source object and relates to the given sense, so
* if the player character cannot sense the source object, then the
* player should not be aware of these happenings and thus should not
* see the messages.
*
* Sense contexts are not nested in their effects - we will show or hide
* the messages that our callback routine generates regardless of
* whether or not messages are hidden by an enclosing sensory context.
* So, this routine effectively switches to the new sense context for
* the duration of the callback, eliminating the effect of any enclosing
* context. However, we do restore the enclosing sense context before
* returning, so there is no lasting net effect on the global sense
* context.
*/
callWithSenseContext(source, sense, func)
{
return senseContext.withSenseContext(source, sense, func);
}
/*
* Sense context output filter. When the sense context doesn't allow
* the player character to sense whatever's going on, we'll block all
* output; otherwise, we'll pass output through unchanged.
*/
senseContext: SwitchableCaptureFilter
/*
* Recalculate the current sense context. We will check to see if
* the player character can sense the current sense context's source
* object in the current sense context's sense, and show or hide
* output from this point forward accordingly. This can be called
* any time conditions change in such a way that the sense context
* should be refigured.
*/
recalcSenseContext()
{
/*
* simply invalidate the cached status; this will ensure that we
* recalculate the status the next time we're called upon to
* determine whether or not we need to block output
*/
cached_ = nil;
}
/*
* Get our current blocking status. If we've already cached the
* status, we'll return the cached status; otherwise, we'll compute
* and cache the new blocking status, based on the current sensory
* environment.
*/
isBlocking()
{
/* if we haven't cached the status, compute the new status */
if (!cached_)
{
/* calculate the new status based on the current environment */
isBlocking_ = shouldBlock();
/* we now have a valid cached status */
cached_ = true;
}
/* return the cached status */
return isBlocking_;
}
/* our current cached blocking status, and its validity */
isBlocking_ = nil
cached_ = true
/*
* Calculate whether or not I should be blocking output according to
* the current game state. Returns true if so, nil if not.
*/
shouldBlock()
{
/*
* Determine if the new sense context allows messages to be
* displayed. If there is no source object, we allow
* everything; otherwise, we only allow messages if the player
* character can sense the source object in the given sense.
*/
if (source_ == nil)
{
/* neutral sense context - allow messages */
return nil;
}
else
{
/*
* Determine if the player character can sense the given
* object. If the source can be sensed with any degree of
* transparency other than 'opaque', allow the messages.
*/
return (libGlobal.playerChar.senseObj(sense_, source_)
.trans == opaque);
}
}
/* invoke a callback with a given sense context */
withSenseContext(source, sense, func)
{
local oldSource, oldSense;
/* remember the old sense and source values */
oldSource = source_;
oldSense = sense_;
/* set up the new sense context */
setSenseContext(source, sense);
/* make sure we restore the old status on the way out */
try
{
/* invoke the callback */
return (func)();
}
finally
{
/* restore the old sense context */
setSenseContext(oldSource, oldSense);
}
}
/*
* Set a sense context.
*/
setSenseContext(source, sense)
{
/* remember the new setings */
source_ = source;
sense_ = sense;
/* calculate the new sensory status */
recalcSenseContext();
}
/* the source object and sense of the sensory context */
sense_ = nil
source_ = nil
;
/* ------------------------------------------------------------------------ */
/*
* Get the current point-of-view actor - this is the actor who's
* performing the action (LOOK AROUND, EXAMINE, SMELL, etc) that's
* generating the current description.
*/
getPOVActor()
{
return libGlobal.pointOfViewActor;
}
/*
* Get the current point of view. In *most* cases, this is the same as
* the point-of-view actor: the actor is looking around with its own
* eyes, so it's the point of view. However, this can differ from the
* actor when the actor is viewing the location being described through
* an intermediary of some kind. For example, if an actor is observing a
* remote room through a closed-circuit TV system, the point of view
* would be the camera in the remote room (not the TV - the point of view
* is intended to be the object that's physically absorbing the light
* rays or other sensory equivalents).
*/
getPOV()
{
return libGlobal.pointOfView;
}
/* get the POV actor, returning the given default if there isn't one set */
getPOVActorDefault(dflt)
{
/* start with the global setting */
local val = libGlobal.pointOfViewActor;
/* if that's not nil, return it; otherwise, return the default */
return (val != nil ? val : dflt);
}
/* get the POV, returning the given default if there isn't one set */
getPOVDefault(dflt)
{
/* start with the global setting */
local val = libGlobal.pointOfView;
/* if that's not nil, return it; otherwise, return the default */
return (val != nil ? val : dflt);
}
/*
* Change the point of view without altering the point-of-view stack
*/
setPOV(actor, pov)
{
/* set the new point of view */
libGlobal.pointOfViewActor = actor;
libGlobal.pointOfView = pov;
}
/*
* Set the root point of view. This doesn't affect the current point of
* view unless there is no current point of view; this merely sets the
* outermost default point of view.
*/
setRootPOV(actor, pov)
{
local stk = libGlobal.povStack;
/*
* if there's nothing in the stacked list, set the current point of
* view; otherwise, just set the innermost stacked element
*/
if (stk.length() == 0)
{
/* there is no point of view, so set the current point of view */
libGlobal.pointOfViewActor = actor;
libGlobal.pointOfView = pov;
}
else
{
/* set the innermost stacked point of view */
stk[1] = pov;
stk[2] = actor;
}
}
/*
* Push the current point of view
*/
pushPOV(actor, pov)
{
/* stack the current one */
libGlobal.povStack.append(libGlobal.pointOfView);
libGlobal.povStack.append(libGlobal.pointOfViewActor);
/* set the new point of view */
setPOV(actor, pov);
}
/*
* Pop the most recent point of view pushed
*/
popPOV()
{
local stk = libGlobal.povStack;
local len;
/* check if there's anything left on the stack */
len = stk.length();
if (len != 0)
{
/* take the most recent element off the stack */
libGlobal.pointOfViewActor = stk[len];
libGlobal.pointOfView = stk[len - 1];
/* take the actor and POV objects off the stack */
stk.removeRange(len - 1, len);
}
else
{
/* nothing on the stack - clear the point of view */
libGlobal.pointOfViewActor = nil;
libGlobal.pointOfView = nil;
}
}
/*
* Clear the point of view and all stacked elements
*/
clearPOV()
{
local len;
local stk = libGlobal.povStack;
/* forget the current point of view */
setPOV(nil, nil);
/* drop everything on the stack */
len = stk.length();
stk.removeRange(1, len);
}
/*
* Call a function from a point of view. We'll set the new point of
* view, call the function with the given arguments, then restore the
* original point of view.
*/
callFromPOV(actor, pov, funcToCall, [args])
{
/* push the new point of view */
pushPOV(actor, pov);
/* make sure we pop the point of view no matter how we leave */
try
{
/* call the function */
(funcToCall)(args...);
}
finally
{
/* restore the enclosing point of view on the way out */
popPOV();
}
}
TADS 3 Library Manual
Generated on 5/16/2013 from TADS version 3.1.3