
*** CSC: Complex Sense Connector ***

TADS 3/Adv 3 Library Extension
Version 1.2
Created by M.D. Dollahite and Steve Breslin

Requires Library Version 3.0.8 or better. Version 3.0.9 or better preferred.


+------------------------------------------+
 Table of Contents

    1. Introduction
    2. Changes
    3. Installation
    4. Using Complex Sense Connectors
    5. More Examples
    6. Known Issues
    7. Translating
    8. License
+------------------------------------------+


******************************************************************************
*  Introduction

    The TADS 3 default library, Adv 3, contains an exciting new system for
improving the realism of simulated environments: the sense connector.  Sense
Connectors allow a character standing in one room to perceive objects located
in another, dismantling the old notion that rooms are separate self-contained
cells and allowing rooms to come together into a larger worldspace.

    The sense connectors included in Adv 3 have a problem, however.  They are
implemented as simple multiple-location objects that allow sense data to pass
between all of their locations omnidirectionally.  Besides having no
directionality, the multi-loc implementation also means that these sense
connectors can appear as tangible objects only in the most trivial cases,
where the same object should appear in all connected rooms.  If an asymmetric
connection is desired, the classes provided by the library are unsuitable.

    The Complex Sense Connector (CSC) extension steps in to fill this gap.  It
allows sense connections to be built from systems of one-way connectors that
exist only in one location, but provide visibility into another. This opens up
a whole range of new possibilities for the use of sense connections, including
robust doors and windows, closed-circuit cameras, walkie-talkies, and so on.


******************************************************************************
*  Changes

1.2
    Corrected a bug in which an opaque window would display it's
    lookThroughDesc as if it were transparent if another sense connector 
    joined the same locations.

    Also added the "shineThru" flag for OneWaySenseConnectors, which controls 
    whether light is transmitted through them. It is appropriate for light to 
    shine through windows, but inappropriate for it to shine through video 
    monitors; this new flag lets you control this behaviour. Modified the 
    sample game to demonstrate this.


1.1
    Moved some code from Window and BasicWindow to the new OpenableWindow 
    class, which simplifies setting up windows that the player can open.
    Fixed bug where LOOKing THROUGH a closed window showed the lookThroughDesc 
    twice. Fixed up the sample game to work a little better.


******************************************************************************
*  Installation

    1. Place the contents of the CSC package in your library folder

    2. Add csc.tl in your TADS 3 project file, after Adv 3 but before
       your game files.

    3. If you want to use the provided templates, #include csc.h right after
       adv3.h and your language-specific header file.
       
    4. If using library version 3.0.8, add the file 308mods.t to your
       project right after Adv 3 and before csc.tl.  This file is
       not necessary for library versions later than 3.0.8.


******************************************************************************
*  Using Complex Sense Connectors

    The CSC extension is made up of four source files.  "csc" merely contains
the module ID, please always include this file in your projects.  "owsc"
defines the OneWaySenseConnector class upon which the rest of the extension is
built.  "obstructor" is optional, it provides a more intuitive method for
making sense connectors point-of-view-aware than the library's Occluder class.
"window" gets you started with one of the most common customizations of the
OneWaySenseConnector, windows; it is also optional.

    The additional file, "308mods", contains a patch for library version 
3.0.8, and is not needed if you're using a later version of the library.


--------------------------------------------
OneWaySenseConnector (owsc.t)

    OneWaySenseConnector is a mix-in class that can be combined with a Thing
subclass to create a one-way sense link.  Actors in the same room as the OWSC
will be able to sense objects in the other locations referenced by the OWSC, 
but Actors in those locations will not be able to see through to the OWSC's 
location.  The "connectionList" property determines what can be sensed through
the connector; the normal Thing "location" property determines where they can
be sensed from.  The "connectorMaterial" property and "transSensingThru" 
method control how transparent the connector is.  Here's an example:

OneWaySenseConnector, Intangible
    location = fromRoom
    connectionList = [otherRoom1, otherRoom2]
    connectorMaterial = glass
;

    An actor in the room "fromRoom" will be able to see into otherRoom1 and
otherRoom2, but actors in either of the other rooms will not be able to see
into fromRoom, at least not through this connector.

    The "connectionList" property must be a list, but does not need to be 
rooms.  For example, you can join two OWSCs together for two-way sense
transmission:

conn1: OneWaySenseConnector, Intangible
    location = room1
    connectionList = [conn2]
    connectorMaterial = glass
;

conn2: OneWaySenseConnector, Intangible
    location = room2
    connectionList = [conn1]
    connectorMaterial = glass
;

    This is how the Window class discussed below works.
    
    OneWaySenseConnector has one other property that can be set. The
"shineThru" property controls whether light travels through the sense 
connection or not. It is true be default, which is appropriate for windows and 
the like, but it can be set to nil for things like video monitors, which do 
not illuminate the location they display. Note that even when this is true, 
the usual rules regarding sense transparency apply (see the adjustBrightness()
function in the library, in "sense.t").


--------------------------------------------
OneWayDistanceConnector (owsc.t)

    OneWayDistanceConnector makes the same customization to 
OneWaySenseConnector that the library DistanceConnector makes to 
SenseConnector.  Set the "location" and "connectionList" properties as usual,
but no connectorMaterial is needed because transSensingThru is already 
overridden to make a DistanceConnector.


--------------------------------------------
Obstructor (obstructor.t)

    In real life, what you can see through a window is determined by the size 
and location of the window relative to the objects on the other side of it.
For example, if a bookcase is pushed against the wall beside the window, you 
probably can't see it looking through the window from the other side.  The 
shape of the window and the direction you're facing restrict your field of 
vision.  To simulate this kind of occlusion, Adv 3 provides the Occluder 
class.  However, Occluder has proven unintuitive due to its disregard for the
containment hierarchy.  Obstructor is a similar mix-in class that does the 
same thing, but does it by pruning entire branches off the sense path as it is 
built, instead of making an extra pass to remove arbitrary objects afterwards.

    Obstructor can be mixed-in with any SenseConnector or OneWaySenseConnector
to provide obstruction capabilities.  Simply override the "obstructObj" method
to return true for any objects you want removed from view.  For example:

Obstructor, OneWaySenseConnector, Intangible
    obstructObj(obj, sense)
    {
        if(sense == sight && obj == bookcase) 
            return true;
        else 
            return nil;
    }
;

    Unlike Occluder, Obstructor can only be mixed with sense connectors, and 
is only useful for window-style occlusion.  Occluder has some other uses that 
Obstructor isn't capable of.

    If you don't need this feature, don't include the module in your build, as
it causes a slight drop in performance on slower computers if the feature is
not used.  If the feature is used, however, it provides better performance
than doing the same thing with Occluder.


--------------------------------------------
BasicWindow (window.t)

    BasicWindow is derived from OneWaySenseConnector, with extra features for 
implementing windows and other such two-way sense connections.  BasicWindow 
also inherits from BasicOpenable and Thing.

    BasicWindows are used in linked pairs, much like Doors, and set their
"connectionList" property automatically to their counterpart in the linked
pair.  To allow for opening and closing the window, BasicWindow also uses the
"material" property in it's default transSensingThru method.  
"connectorMaterial" controls transparency when the window is open; the
transparency of a closed window is given by the compounding of both materials.
Thus, you may think of "connectorMaterial" as the transparency of the air on 
that line of vision, and "material" as the transparency of the pane or
shutters.

    BasicWindow provides a property named "lookThroughDesc", which can be used
to describe what is seen through the window.  The default implementation uses
lookAroundWithin to produce a normal description of the room on the other side
of the window.  You can use "roomRemoteDesc" and the other various "remote"
description features to customize this, or you can override lookThroughDesc to
provide a simple static description (though the latter somewhat defeats the
purpose of the BasicWindow class.)  If you want to display a look through
response programmatically, call the window's "lookThrough" method with the
actor doing the looking as the only argument.  This method is called 
automatically when the object is Examined, after the regular long description.


--------------------------------------------
Window (window.t)

    As implied by the name, BasicWindow does not handle any Actions.  Window
adds handling for LookThrough, and  can be combined with Openable to handle
Open and Close. Openable should be placed to the right of Window in the 
superclass list to make sure inheritance works out right.

    Here's an example, using the templates defined in "csc.h":

southWindowSide: Window, OneWayDistanceConnector, Fixture
    'window' 'window' @southRoom
    "It's a plain white-trimmed window in the north wall. "
;

northWindowSide: Window, OneWayDistanceConnector, Fixture
    ->southWindowSide 'window' 'window' @northRoom
    "It's a plain white-trimmed window in the south wall. "
;

    The -> part of the template sets the "masterObject" property, just as it
does with most other classes derived from Linkable.

--------------------------------------------
OpenableWindow (window.t)

    OpenableWindow mixes Window and Openable together for you, and also adds 
handling to automatically LOOK THROUGH a window when opening it (just as 
Container automatically LOOKs IN when opened).

    This class also includes a "remote open" notifier like in Door, which 
announces when the window is opened from the other side.


******************************************************************************
*  More Examples

--------------------------------------------
A webcam:

webcam: Thing 'webcam/cam' 'webcam'
    "It's a battery-powered wireless webcam. "
;

pda: OneWaySenseConnector, Thing 'pda' 'PDA'
    "It displays the feed from the webcam. "
    connectionList = [webcam]
    connectorMaterial = glass
    shineThru = nil
    examineStatus()
    {
        inherited;
        webcam.lookAround(gActor, 
            LookRoomDesc|LookListSpecials|LookListPortables);
    }
;

    NOTE: Some odd things may happen if the PDA is seen from a distance or in
other special situations, so additional considerations may be needed on the 
PDA object to make everything work right in such cases. Since the solution to
these problems is usually case-specific, they are left as an excercize for the
reader. One method of solving them is demonstrated in the sample game.


--------------------------------------------
A look-out tower:
    
OneWayDistanceConnector, Distant 'forest' 'forest' @lookOutTower
    "You can look out over the entire forest from here. "
    
    sightSize = large

    connectionList = [forest1, forest2, forest3, forest4]

    examineStatus()
    {
        fromPOV(gActor, &lookAroundWithin, gActor,
            gActor, LookListSpecials|LookListPortables);
    }
;

    This will respond to an EXAMINE FOREST command with the connector's 
description following by a listing of the contents of the four forest rooms.
With the right inRoomName overrides, this will make the entire forest appear
more or less as a single object from the tower, but as separate rooms from
within the forest itself.


******************************************************************************
*  Known Issues

  * Although a pair of BasicWindow objects are mostly made to act as two sides 
    of a single physical object, they do not share their "material" or
    "connectorMaterial" properties. Although this may sometimes be counter to
    expectations, it is considered a feature, not a bug, because it allows for 
    such things as one-way mirrors.

  * When two sense connectors (either CSC or library) connect the same two 
    locations and are both opaque to touch, attempting to reach an object in
    the other location will choose one of the two connectors arbitrarily as
    the obstructing object. With intangible library connectors this isn't 
    really a problem, but with tangible CSC connectors this sometimes results 
    in illogical actions; for example, the included sample game will often try 
    to reach objects through the PDA instead of the window. There does not 
    appear to be any way to solve this problem short of completely rewriting 
    the library's sense path algorythm. Since that is unlikely to happen, the
    best solution is probably to replace the appropriate library messages with 
    alternatives that don't mention which object is causing the obstruction.

  * It has been suggested that descriptions of object should be prefixed with
    a mention of the sense connector they are seen through. Unfortunately
    the current design of the library's sense path algorythm does not provide 
    objects with any information on the path used to reach them, so this is
    not possible unless that limitation is removed.

  * Theoretically, it should be possible to combine a Door with a Window or
    OpenableWindow to make a door you can look through, but this is currently
    untested.


******************************************************************************
*  Translating

    This extension is mostly internal logic and contains very few strings.
Currently, only the "window" modules contains strings (aside from the module 
ID, which even the Adv3 library does not seem to consider a language-specific
component). As there are only a four of them, I did not deem it necessary to 
create a separate language module; just use a modify statement in your project 
to replace them.


******************************************************************************
*  License

    TADS 3 Complex Sense Connector Extension Copyright 2004 M.D. Dollahite and
Steve Breslin. All Rights Reserved.

    This extension may be freely distributed as long as all files remain
intact and unaltered. It may be used in any TADS 3 game without restrictions.
If used in a game, we'd appreciate a mention in the credits, but it is not
required.

    If you wish to create a port or other derivative work, contact the authors
for permission. At time of writing, their email addresses are:

    M.D. Dollahite: ryukage@aol.com
    Steve Breslin: versim@hotmail.com

    If contact cannot be established by email, try the internet newsgroup
<rec.arts.int-fiction>.

    This software is provided "as is" without warranty of any kind, express or
implied, including, but not limited to, the implied warranties of
merchantability and fitness for a particular purpose or a warranty of
non-infringement.

