I’ve found that I frequently have situations where I want to change how things are referred to based on game state/player knowledge. This isn’t a particularly big deal to handle as a bunch of individual one-offs, but it was coming up enough that I decided to throw together a little library/module to handle it.
The basic idea is that you might have a room (or whatever) that’s initially identified as “A Dark Cave” or something when the player first sees it, but then later they discover it’s actually “Entrance To The Secret Hideout” or something like that.
Here I’m controlling the behavior using a simple gRevealed()
check, but it would be easy enough to extend this to more complex checks. Anyway, here’s a demo with all the library code inlined:
#charset "us-ascii"
#include <adv3.h>
#include <en_us.h>
// Enum containing all our dynamic word types.
enum dWord, dTitle;
// Preinit object that we use as a container for methods.
dynamicWords: PreinitObject
// A lookup table indexed by key (the one used for gRevealed())
// containing all our DynamicWord instances.
_table = nil
// Setup our lookup table of words
execute() {
_table = new LookupTable();
forEachInstance(DynamicWord, function(o) {
_table[o.id] = o;
});
}
// Return the current word matching the given ID and, optionally,
// of the type specified in the flags.
getWord(id, flags?) {
local o;
o = _table[id];
if(o == nil)
return(nil);
// We don't do anything fancy here, yet.
switch(flags) {
case dTitle:
return(o.getWordAsTitle());
default:
return(o.getWord());
}
}
;
// Class to hold all the stuff for a single dynamic word.
class DynamicWord: object
id = nil // key to use for gRevealed()
word = nil // "revealed" basic form of the word
wordAsTitle = nil // "revealed" word formatted as a title
initWord = nil // "unrevealed" basic form of the word
initWordAsTitle = nil // "unrevealed" word formatted as a title
construct(n, w0?, w1?, l0?, l1?) {
id = n;
initWord = (w0 ? w0 : nil);
word = (w1 ? w1 : nil);
initWordAsTitle = (l0 ? l0 : nil);
wordAsTitle = (l1 ? l1 : nil);
}
// Return the basic form of the word. Every instance has to have
// something defined for word and initWord.
getWord() { return(gRevealed(id) ? word : initWord); }
// Return the word formatted for use as a title, e.g. in a room name.
getWordAsTitle() {
return(gRevealed(id) ? wordAsTitle : initWordAsTitle);
}
;
// Template for the DynamicWord class.
DynamicWord template 'id' 'initWord'? 'word'?;
// Convenience macro for defining a DynamicWord instance.
// Creates global functions to make referring to instance simpler:
// if the ID is "foo", fooWord() will return the basic form of the word
// and fooWordAsTitle() will return the word formatted as a title (to be
// used in for example a room name).
#define DefineDynamicWord(id, name, init, reveal...) \
id##Word() { return(id##DynamicWord.getWord()); } \
id##WordAsTitle() { return(id##DynamicWord.getWordAsTitle()); } \
id##DynamicWord: DynamicWord name init reveal
// Convenience macros for accessing the dynamic words by their keys.
#define dWord(key, args...) dynamicWords.getWord(key, args)
#define dWordAsTitle(key, args...) dynamicWords.getWord(key, title)
// Creates an object called voidDynamicWord and global functions
// voidWord() and voidWordAsTitle(). voidWord() will return
// 'unknown void' if gRevealed('voidFlag') returns nil and 'formless void'
// if it returns true. voidWordAsTitle() will return 'Void of Some Kind'
// and 'Formless Void', also testing gRevealed('voidFlag').
DefineDynamicWord(void, 'voidFlag', 'unknown void', 'formless void')
initWordAsTitle = 'Void of Some Kind'
wordAsTitle = 'Formless Void'
;
// The room name and description use the dynamic word we defined above.
startRoom: Room '<<voidWordAsTitle()>>'
"This is a <<voidWord()>>. There's a plaque on the wall. "
;
// Examining the sign sets voidFlag.
+Fixture 'plaque/sign' 'plaque'
"The plaque identifies this room as the Formless Void.
<.reveal voidFlag> "
;
+me: Person;
versionInfo: GameID
name = 'sample'
byline = 'nobody'
authorEmail = 'nobody <foo@bar.com>'
desc = '[This space intentionally left blank]'
version = '1.0'
IFID = '12345'
;
gameMain: GameMainDef
initialPlayerChar = me
;
The interesting bits (from a game implementor’s standpoint) are that you can use something like:
DefineDynamicWord(void, 'voidFlag', 'unknown void', 'formless void');
Where the args in this specific example are:
-
void is used to create a couple convenience methods of the form [arg] +
Word
and [arg] +WordAsTitle
, in this casevoidWord()
andvoidWordAsTitle()
-
voidFlag is the single-quoted string to use for the
gRevealed()
check - unknown void is the single-quoted string to use for the “unrevealed” word
- formless void is the single-quoted string to use for the “revealed” word
…and then anywhere in the game code voidWord()
can be used and will return the appropriate version based on whether or not voidFlag
has been revealed. In addition, the initWordAsTitle
and wordAsTitle
properties can be declared and then accessed via voidWordAsTitle()
. This is intended to hold the word/phrase formatted as a “title”, as for example used in a room title. E.g.:
DefineDynamicWord(void, 'voidFlag', 'unknown void', 'formless void')
initWordAsTitle = 'Void of Some Kind'
wordAsTitle = 'Formless Void'
;
startRoom: Room '<<voidWordAsTitle()>>'
"This is a <<voidWord()>>. There's a plaque on the wall. "
;
+Fixture 'plaque/sign' 'plaque'
"The plaque identifies this room as the Formless Void.
<.reveal voidFlag> "
;
Gives us a transcript like:
Void of Some Kind
This is a unknown void. There's a plaque on the wall.
>x sign
The plaque identifies this room as the Formless Void.
>l
Formless Void
This is a formless void. There's a plaque on the wall.
In addition to using the dynamically created functions (in this case voidWord()
and voidWordAsTitle()
) you can do the same thing via dWord('voidFlag')
and dWord('voidFlag', dTitle)
, respectively. That is, access the current value(s) for the DynamicWord
instance by key instead of by their “personal” function names.
This is extremely simple stuff, but it’s a thing that has come up a lot in implementing a mystery-ish game. So I figured I’d throw it out there in case it’s useful for anyone else.
The code is available in the form of a T3 module is available from a github repo and will almost certainly be updated as I try to hammer my WIP into shape.