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:

The library defines a special nullObj object that can be used in the third case to give the description "That's not something you need to refer to." This could be useful, for example, it discouraging players from exploring sub-objects of sub-objects and the like.

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:

If you do want to use message substituton parameters in your custom notImportantMsg, there are a couple of points to note:

  1. 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.
  2. 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.