Scenery
Overview
The scenery.t module allows the creation of multiple decorations from a single object.
If a location mentions or implies the existence of a set of Decoration objects, you would normally need to do something like this:
+ Decoration 'sun; bright' "The sun is quite bright today. " ; + Decoration 'sky; blue clear' "The sky is mostly clear and blue, but there are the odd patches of fluffy cloud. " ; + Decoration 'clouds; fluffy white' "A trio of fluffly white clouds drift lazily towards the west. " ;
The scenery module lets you compress this to:
+ Scenery [ ['sun; bright', 'The sun is quite bright today. '], ['sky; blue clear', 'The sky is mostly clear and blue, but there are the odd patches of fluffy cloud. '], ['clouds; fluffy white', ''A trio of fluffly white clouds drift lazily towards the west. ' ] ]
Which may feel more of a saving of busy-work the more such decoration objects you would otherwise have to create. .
For many purposes, that may be as much as you need to know about the Scenery class, but there is more you can do with it than that, which we shall now go on to explore. Perhaps most importantly, the Scenery class not only lets you create a set of Decoration objects with a bit less effort than defining them all individually, it also lets you manipulate a related set of decorations more easily.
The Scenery Class
The Scenery class works by generating a series of decoration elements (actually of the special class ScenItem) based on the information supplied in its scenList property (note that there is probably no good reason for game code ever to define its own ScenItem objects - these are entirely handled by the library). In the example above the scenList was defined implicitly via the Scenery template. There may, however, be occasions when you may want to modify the scenLitem class, for example if you want to override its decorationActions property to include a custom action you have created.
The scenList should contain a list of sublists, with each sublist defining a different decoration object. Each sublist can contain either two or three eleements thus:
+ Scenery [ [vocab, desc], [vocab, desc, notImportantMsg] ]
The vocab element contains the vocab string to be assigned to the decoration object, using precisely the same format as for defining the vocab property of any Thing.
The desc element can be one of:
- A single-quoted string containing the decoration's description.
- An anonymous function or floating method to output the decoration's description.
- An object whose descStr property will be used for the decoration's description.
The optional notImportantMsg element defines the (not important) message to be displayed when the player tries to do anything but EXAMINE with this object. The notImportantMsg can be speficied as one of:
- A single-quoted string containing the not important message.
- An object whose notImportantMsg will be used. Note that if the second list element is an object and the third element is not specified, the second object will be used here too (this allows us to write, say, ['air', nullObj] rather than the repetitive ['air', nullObj, nullObj]). You can also use the name of a class here, such as Distant to display "The whatever is too far away."
- An integer, in which case the corresponding item in the Scenery object's notImportantMsgLst list property. For example, if in the above example we had written ['sun; bright', 'The sun is quite bright today. ', 2], then the notImportantMsg for our sun decoration would be than given in notImportantMsgLst[2]. This is principally intended for cases where s Scenery object has a relatively long list of itmes in its scenList and needs to alternate between a small number of different not imporant messages it would be needlessly repetitive to keep typing on individual items.
- A property pointer (e.g., &mySpecialNotImportantMsg), in which case the notImportantMsg will be taken from that defined on the corresponding custom property/method of the Scenery object. This custom property can be defined as a single-quoted string or a method that returns a single-quoted string, which may be a better option if it includes message substitution parameters.
- Nothing at all (i.e. the third element is omitted), in which case the second element's notImportantMsg will be used (if the second element is an object) or else the Scenery object's notImportantMsg will be used (if your Scenery object defines one) or, failing that, the library's default notImportantMsg for decorations ("The whatever is not important. ") will be used. If the majority of the scenery items your Scenery object is defining are distant, you might usefully define notImportantMsg = delegated Distant on the Scenery object to that it's default notImportantMsg becomes "The whatever is too far away. "
If you do want to use message substituton parameters in your custom notImportantMsg, there are a couple of points to note:
- The parameter you need to refer to the decoration item in question is {cobj}. For example you could write a notImportantMsg as '{The subj cobj} {is} utterly unimportant.' or 'There is no need to meddle with {the cobj}. ' which should be usable on multiple decoration objects, with the name of the object replacing {the subj cobj} or {the cobj} in each case.
- You may encounter cases in which the parameter substitution does not appear to work properly, especially if your notImportantMsg is defined as a single-quoted string (this can arise because the parameter substitutions may be evaluated prematurely). In such a case the solution may be to replace the single-quoted string with a method, but this will often not be necessary.
Another point to not is that while we can make a Scenery object's decoration objects use the Distant class's notImportantMsg ('The whatever is too far away' ), such objects wo't have the full functionality of the Distant class, in particular when it comes using the GO TO command with a Distant object. For many casees this won't metter, but if you do want GO TO to work with a dedoration object you'll need to create a separate Distant object for it.
The example we started with involves the sky and objects in the sky. If our game switches between day and night we may want to replace one set of sky objects with another as day turns to night and vice versa, for example:
+ daySky:Scenery [ ['sun; bright', 'The sun is quite bright today. '], ['sky; blue clear', 'The sky is mostly clear and blue, but there are the odd patches of fluffy cloud. '], ['clouds; fluffy white', ''A trio of fluffly white clouds drift lazily towards the west. ' ] ] notImportantMsg = delegated Distant ; + nightSky:Scenery [ ['moon; full', 'The moon is full tonight. '], ['sky; dark' 'The sky is dark, but mostly clear, so you can see a myriad stars. '], ['stars;;constellations; them', 'The stars shine brightly, forming many familiar constellations', 'The stars are way out of your reach. ' ] ] notImportantMsg = delegated Distant initiallyPresent = nil visibleInDark = true ;
Note how we define notImportantMsg = delegated Distant on both Scenery objects to get a more appropriate default notimportantMsg ("The sky is too far away. " rather than "The sky is not important. "). Note also that we have defined initiallyPresent = nil on the nightSky Scenery object. If our game starts in daylight, then we want the daytime sky decoration objects to be initially present but not the time ones, and it's the initiallyPresent property (which is true by default) that controls this. To swap which set of Scenery objects is preeent we can then use the makePresent(stat) method.
daySky,makePresent(nil);
night.sky.makePresent(); // or nightSky.makePresent(true)
As should be apparent from this example, if the stat parameter is true (or omitted) then the Scenery's decoration objects are brought into the Scenery object's location; otherwise, if it is nil, they are moved off-stage (equivalent to calling daySky.moveInto(nil)).<.p>
Note, too, that we define nightSky's visibleInDark property to be true; this ensures that all the decoration objects the nightSKy Scenery object creates will have visibleInDark set to true, which well be needed if we make all the outside loatioss go dark after nightfall (even on a dark night the sky, moon and stara remain visible).
Finally, if we need to tweak the Scenery's decoration objects any further, they are available through the Scenery object's myObjs property, which contains a list of the decoration objects created by the Scenery object, in the order in which they're listed in the scenList property, so that, for example nightSky.myObjs[1] would give us a reference to the moon object while nightSky.myObjs[2] would give us the sky object. This potentially allows us to do any further fine-tuning of these decorations that's not already provided for by the library (although hopefully, that shouldn't be necessary all that often). Beyond a certain level of tweaking and complexity it may be easier to creaate ordinary individual Decoration objects.
The MultiLocScenery Class
The examples we have looked at above assume that all the decorations created by our Scenery objects exist in a single location (or Room), but unless our game has only one outdoor location, the chances are that we'll want decodation objects like sun, clouds and sky to appear in multiple locations, that is, to act at MultiLocs. But it won't work to try defining a Scenery object with a mix-in as MultiLoc, Scenery, since it's not the Scenery object we need to be in multiple places, it's the decoration objects it creates. To achieve that we instead need to use the MultiLocScenery class.
We can then set up our MultiLocScenery objects in almost exactly the same way as we set up our Scenery objects, for example.
+ daySky:MultiLocScenery [ ['sun; bright', 'The sun is quite bright today. '], ['sky; blue clear', 'The sky is mostly clear and blue, but there are the odd patches of fluffy cloud. '], ['clouds; fluffy white', ''A trio of fluffly white clouds drift lazily towards the west. ' ] ] notImportantMsg = delegated Distant intialLocationList = [outdoors] ; + nightSky:MultiLocScenery [ ['moon; full', 'The moon is full tonight. '], ['sky; dark' 'The sky is dark, but mostly clear, so you can see a myriad stars. '], ['stars;;constellations; them', 'The stars shine brightly, forming many familiar constellations', 'The stars are way out of your reach. ' ] ] notImportantMsg = delegated Distant initiallyPresent = nil visibleInDark = true intialLocationList = [outdoors] ;
Where oudoors might be a Region we've created to contain all our outdoor locations, and intialLocationList has the same meaning as it does on the MultiLoc class. Indeed you can use all the same methods and properties on MultiLocScenery to set up where its MultiLoc Decorations start out as you can on the MultiLoc class: intialLocationList or locationList, intialLocationClass and exceptions, as well as the isInitiallyIn(loc) method that can be used in conjunction with initialLocationClass (as on MultiLoc), and these will work to define the locations of the decoration objects generated by the MultiLocScenery object.
You can also call the methods moveInto(loc), moveIntoAdd(loc) and moveOutOf(loc) on a MultiLocScenery object and these will be applied to all its decoration objects (by calling the same method on each of them in turn), with the same effect as they would have on any MultiLoc item. Amongst other things, this could prove very useful for manipukated groups of related MultiLoc decorations.
Note that MultiLocScenery inherits from the Scenery class, so it also provides all the properties and methods of that class described above.
Finally, note that the MultiLocScenery generates objects of the MultiScenItem class.