#charset "us-ascii"
#include "advlite.h"
/*
*. Brightness Exteension for adv3Lite
*. Version 1.0 29-Oct-22
*. Eric Eve
*.
* This extension provides much of the adv3 ligthting levels functionality in a slightly different
* form for the adv3Lite library.
*
* It adds brightness and opacity properties to the Thing class, as well as a brightnessWithin()
* method which returns the current brightness within a room or booth (which is taken to be the
* brightness of its brightest available light source). The opacity property takes effect only on
* a container that is defined as transparent, in which case the opacity is the loss of brightness
* for a light source shining in or out of that container.
*
* The extension is set up to mimic the adv3 brightness scale, in that a brightness of 0 equates
* to darkness, a brightness of 1 is a self-illuminating object (or isVisibleInDark) that doesn't
* illuminate anything else, a brightness of 2 provides dim light that's sufficient for examining
* one's environment but not enough to read by, a brightness of 3 is normal light, and a
* brightness of 4 is an especially bright light, but game code can implement a differnent scale
* if desired.
*
* There are limitations to the realism of this brightness model, for example it doesn't
* accumulate the brightness of multiple light sources, and offers only limited handling of light
* sources in remote locations in the same SenseRegion. Also, apart from imposing a minimum
* brightness level required for reading and providing the means to model loss of brightness at a
* distance or through a semi-transparent medium, it has very little impact on game behaviour out
* of the box; for the most part, it is up to game code to decide what it wants to do with the
* brightness mechanism provided.
*/
modify Thing
/* Our brightness when lit [BRIGHTNESS EXTENSION] */
brightnessOn = 3
/*
* Our brightness when unlit. This would normally be 0, but if we're visible in the dark it
* will be 1. [BRIGHTNESS EXTENSION]
*/
brightnessOff = (visibleInDark ? 1 : 0)
/*
* [BRIGHTNESS EXTENSION]
*
* The strength of the light the object is giving off, if indeed it is giving off light. This
* value should be one of the following:
*
* 0: The object is giving off no light at all.
*
* 1: The object is self-illuminating, but doesn't give off enough light to illuminate any
* other objects. This is suitable for something like an LED digital clock.
*
* 2: The object gives off dim light. This level is bright enough to illuminate nearby
* objects, but not enough to go through obscuring media, and not enough for certain
* activities requiring strong lighting, such as reading.
*
* 3: The object gives off medium light. This level is bright enough to illuminate nearby
* objects, and is enough for most activities, including reading and the like. Traveling
* through an obscuring medium reduces this level to dim (2).
*
* 4: The object gives off strong light. This level is bright enough to illuminate nearby
* objects, and travel through an obscuring medium reduces it to medium light (3).
*
* There is nothing to stop game code using a higher value still to model a a super-powerful
* light source if that seems suitable to the situation being modelled in the game, but this
* probably will only rarely be necessary.
*
* Note that the special value -1 is reserved as an invalid level, used to flag certain events
* (such as the need to recalculate the ambient light level from a new point of view).
*
* Most objects do not give off light at all.
*
* Return the appropriate on/off brightness, depending on whether or not we're currently lit
*/
brightness = (isLit ? brightnessOn : brightnessOff)
/*
* [BRIGHTNESS EXTENSION]
*
* Our opacity is the extent to which we reduce the brightness of any light that passes
* through us. An opacity of 4 or more will cut off the brightest light, while an opacity of 0
* means we're transparent. By default we have an opacity of 0 if we're transparent and 4
* otherwise.
*
* Note that if we want any light to penetrate us at all we should set transparency to true
* (we're at least somewhat light permeable) and then set opacity to some suitable value (if
* we don't want it to be zero, which is otherwise the default for a transparent object).
*/
opacity = (isTransparent ? 0 : 4)
/*
* [BRIGHTNESS EXTENSION]
*
* Our remote brightness when viewed from pov, where pov is in a remote location. By default
* we just return our brightness, but game code may wish to override when, for example, we're
* a torch/flashlight that's been dropped on the far side of a field at night.
*/
remoteBrightness(pov)
{
return brightness;
}
/*
* How bright is it within us (assuming, in practice, that we're either a Room or an enterable
* container (aka Booth)? [BRIGHTNESS EXTENSION]
*/
brightnessWithin()
{
/* Move a light probe inside us. */
lightProbe_.moveInto(self);
try
{
/* Set up a local variable to hold our brightness within and initialize it to zero. */
local bw = 0;
/*
* Set up a local variable to hold a list of available light sources, for possible use
* by game code which wants to develop an algorithm for accumulating the overall
* brightness of multiple sources.
*/
local lightSources_ = new Vector(10);
/* Cache the scopeList of our lightProbe object. */
local sl = Q.scopeList(lightProbe_).toList();
/*
* If we've included the SensseRegion extension and our enclosing room is in at least
* one SenseRegion, then add all the potential light sources in all the rooms in all
* the SenseRegions we're located in to our scope list.
*/
if(defined(QSenseRegion)
&& valToList(lightProbe_.getOutermostRoom().regions).indexWhich(
{r: r.ofKind(SenseRegion)}))
{
foreach(local reg in getOutermostRoom.regions)
{
if(reg.ofKind(SenseRegion) && reg.canSeeAcross)
{
foreach(local rm in reg.roomList)
{
foreach(local o in rm.allContents)
{
if(o.remoteBrightness(lightProbe_) > 1)
sl = sl.appendUnique([o]);
}
}
}
}
}
/* Iterate over the light probe's scope list */
foreach(local obj in sl)
{
/*
* Note the object's brightness, which is simply the value of its brightness
* property if its in the same room as us, or otherwise the value returned by its
* remote brightness method.
*/
local bt = obj.isIn(getOutermostRoom) ? obj.brightness : obj.remoteBrightness(self);
/*
* Create a local variable to contain a list of objects that might potentially
* come between the obj and the light probe.
*/
local blockers;
/*
* If obj has a brightness (adjusted for remoteness if necessary)) that's greater
* than our illuminationThreshold and the obj is in the same room as the light
* probe, then we need to look for objects that might intervene in the sight path.
*/
if(bt > illuminationThreshold && obj.isIn(lightProbe_.getOutermostRoom))
{
/*
* First get the lists of objects that might lie between obj and the light
* probe.
*/
blockers = lightProbe_.containerPath(obj);
/*
* If we have the same container (blocker[2] == self] then no objects
* intervene and there's nothing more we need to do, so we only need to do
* more if we have different containers.
*/
// if(blockers[2] != self)
{
/*
* Iterate outwards over all the objects that might intervene between the
* lightProbe and obj up to their common container.
*/
foreach(local cur in blockers[1])
{
/* We're only interested in objects that are closed containers */
if(cur.contType == In && !cur.isOpen)
/*
* if we find one, deduct its opacity from the brightness
* lightProbe_ picks up from obj.
*/
bt -= cur.opacity;
}
/* Then do the same iterating inwards. */
foreach(local cur in blockers[3])
{
if(cur.contType == In && !cur.isOpen)
bt -= cur.opacity;
}
}
}
/*
* Otherwise, if obj is in a remote location but has a remote brightness > 1,
* interate outwards from obj to its containing room and inwards from the
* lightprobe's containing room to the light probe.
*/
else if(bt > 1)
{
blockers = obj.containerPath(obj.getOutermostRoom);
foreach(local cur in blockers[1])
{
if(cur.contType == In && !cur.isOpen)
bt -= cur.opacity;
}
blockers = getOutermostRoom.containerPath(lightProbe_);
foreach(local cur in blockers[3])
{
if(cur.contType == In && !cur.isOpen)
bt -= cur.opacity;
}
}
/*
* If we've found a brightness that's greater than that of any previous object's
* brightness, update our potential brightness within to that object's brightness.
*/
if(bt > bw)
bw = bt;
/*
* Add both the object and its adjusted brightness to our list of lightSources,
* provided the adjusted brightness exceeds our brightness threshold.
*/
if(bt > illuminationThreshold)
lightSources_ += [obj, bt];
}
/*
* Convert the lightSources_ Vector to a list and store it in our lightSources
* property.
*/
lightSources = lightSources_.toList();
/*
* Provided the highest brightness level we've found is greater than zero, return that
* brightness level, otherwise return zero
.*/
return (bw > 0 ? bw : 0);
}
/* Clean up by ensuring we remove the light probe from the game map. */
finally
{
lightProbe_.moveInto(nil);
}
}
/*
* The list of lightSources and their adjusted (for opacity and/or distance) brightness
* generated by the most recent call to brightnessWithin(). Each element in the list is itself
* a two-element list of the form [obj, adjustedBrightness] where obj is the object providing
* light and adjustedBrightness is the brightness of that object adjusted for transmission
* through distance or partial opacity. [BRIGHTNESS EXTENSION]
*/
lightSources = []
/*
[BRIGHTNESS EXTENSION]
This is little more than a hook for user code to provide its own means of accumulating the
brightness from multiple light sources.
*/
accumulatedBrightnessWithin()
{
/*
* First call the brightessWithin() method to ensure we update the lightSources property
* and also so we can store the value as it returns as the maximum brightness of any
* individual available light source.
*/
local maxBrightness = brightnessWithin();
/*
* Return the value of our accumlateBrightness() method. By default this will simply be
* our maxBrightness.
*/
return accumulateBrightness(maxBrightness);
}
/*
* [BRIGHTNESS EXTENSION] The accumulateBrightness() method is a stub (or hook) for a
* user-defined algorithm for accumulating the brightness of multiple ligjt sources if this is
* desired. By default we simply return the value of the maxBrightness parameter that is
* passed to us (which is the brightness returned by the latest call to brightnessWithin()),
* but game code can override this method to provide some other calculation of accumulated
* brightness by iterating over the list of objects and their adjusted brightnesses in our
* lightSources property.
*
* Note that accumulateBrightness is provided to allow it to be readily overridden by game
* code, but is not designed to be directly called from game code, which should call it only
* via accumulatedBrightnessWithin();
*/
accumulateBrightness(maxBrightness)
{
return maxBrightness;
}
/*
* [BRIGHTNESS EXTENSION]
*
* Change the definition of having sufficient light to see by to have an accumulated brightess
* within greater than our illumination threshold. We only do this if the light probe is
* off-stage, however, since otherwise we'll cause a stack overflow via a circular reference
* while the brightnessWithin is being calculated; if the lightProbe_ is in use we accordingly
* fall back on the inherited handling. (This will need looking at since it prevents
* recogition of a light source in a remote location in the same SenseRegion).
*/
isIlluminated()
{
if(lightProbe_.location == nil)
return accumulatedBrightnessWithin() > illuminationThreshold;
else
return inherited();
}
/*
* For the purposes of the BRIGHTNESS EXTENSION, litWithin() should return the same result as
* isIlluminated.
*/
litWithin()
{
return isIlluminated();
}
/*
* [BRIGHTNESS EXTENSION]
*
* The illumination threshold is the available brightness (returned by the brightnessWithin
* method) that needs to be exceeded in our interior to be able to examine objects or look
* around or satisfy other visibility criteris (mostly where the objVisible precondition is
* applies to the current action). The default illuminationThreshold is 1, which mimics the
* behaviour of both the adv3 library and the adv3Lite library in the absence of this
* extension.
*/
illuminationThreshold = 1
/*
* The brightness needed for us to be able to read this object (as opposed to merely examine
* it) By default we'll set this at 3 (the brightness used by adv3) but game code can overrife
* this to some other value if desired. [BRIGHTNESS EXTENSION]
*/
brightnessForReading = 3
/*
* If the available light is less than the light we need to read this item (its
* brightnessForReading) then stop the read action at the check stage by displaying our
* tooDarkToReadMsg.
*/
dobjFor(Read)
{
check()
{
inherited();
if(interiorParent.accumulatedBrightnessWithin() < brightnessForReading)
say (tooDarkToReadMsg);
}
}
/* The message to display if there's not enough light to read this item. [BRIGHTNESS EXTENSION]*/
tooDarkToReadMsg = BMsg(too dark to read, 'There{\'s} not enough light {here} to read
{the dobj}. ');
;
/*
* Test object used in the measurement of the brightness level within a container. [BRIGHTNESS
* EXTENSION]
*/
lightProbe_:Thing
;
Adv3Lite Library Reference Manual
Generated on 25/04/2024 from adv3Lite version 2.0