The method Actor.trackFollowInfo() is now invoked from
Actor.beforeTravel() rather than directly from
traveler.travelerTravelTo(), as it was in the past. This change
makes the follow-tracking a lot less "special," in that it uses the
general before/after travel notification system rather than a
separate, purpose-built notification system. Note that this change
means that any actor object that overrides beforeTravel() must
inherit the base class implementation if the actor wants to be able
to follow other actors.
A new property, globalParamName, has been added to Thing. This lets
you define a substitution parameter string (for "{xxx}" sequences)
that can be used at any time to refer to the object in message text.
Once you define an object as having a global parameter name, you can
use that name to refer to the object in {xxx} sequences, even when no
command is active, and even when the object isn't involved in any
command. This can be especially useful for writing messages that
refer to objects whose names change in the course of the game, such as
actors who are known by one name until introduced, then are known by
another name ("the white-haired man" might become "Bob" after he tells
us his name, for example).
A new Script subclass, EventList, provides a convenient way of
defining scripts using procedural code in steps without writing a big
'switch' statement in the doScript() method. The eventList property
contains a list of script step elements. Each element gives one step
of the script. An element can be a single-quoted string, in which
case the string is simply displayed; a function pointer, in which case
the function is invoked with no arguments; a ScriptEvent object, in
which case the object's doEvent() method is invoked; or nil, in which
case nothing happens on the step.
The Actor method getDefaultInterlocutor() has been renamed to
getCurrentInterlocutor(), to better reflect its purpose. The
conversation model keeps track of the actor we're currently talking
to; conversational commands that aren't directed to a specific actor
(such as ASK ABOUT BOOK) are assumed to be directed to the current
conversational partner.
A couple of the abstract base classes in the area of Container have
been renamed to better reflect their purposes. The class formerly
known as BasicContainer is now called BulkLimiter, and the class
formerly known as Encloser is now called BasicContainer. So, Container
is a BasicContainer, which is a BulkLimiter; Surface is also a
BulkLimiter. The new name BulkLimiter is more consistent with its
main purpose, which is to constrain the aggregate bulk of its
contents. The old name "Encloser" was confusingly similar to the
real word "Enclosure," and was vague in conveying how the class
differs from Container; the new name BasicContainer makes it clearer
that this class contains some of the basic abstract functionality
of Container but
The match list for an ALL phrase in player input is now filtered
through the filterResolveList() method of each object in the list,
just as ambiguous noun phrases are. This allows objects that
substitute for others in resolution lists (such as Collective and
CollectiveGroup objects) to perform the same substitutions in ALL
lists that they would in normal matches.
The miscWord grammar rules are now defined in en_us.t, rather than
in the language-independent parser.t as they were previously. This
change is necessary because different languages might have different
token types that are valid in miscellaneous word lists. (In point of
fact, the English parser defines a special language-specific token,
tokApostropheS, that is now allowed in miscellaenous word lists.)
The menu system now uses the regular game color scheme as the
defaults; the top "instructions" bar is shown using the status line
color scheme, and the main menu area uses the normal game window color
scheme. This scheme is the safest default, since it uses only colors
the user has selected (on interpreters that allow the user to select
the color scheme); this ensures in particular that things like
hyperlink colors work well with the menu colors, since presumably the
user will have chosen settings that work well together. (If not, at
least it's not the game's fault.) This change means that menus won't
stand out as well from the main game window, since they use the same
color scheme, so the default mode for menus has been changed to the
"full screen" mode. This new default appearance - full screen, using
normal game and status-line colors - makes menus very unobtrusive,
since they just look like an ordinary game screen.
The menu system takes advantage of the new MORE-mode banner style
option, by using a separate banner to show "long topic" items. Since
long text can now safely be displayed in a banner, with the interpreter
providing pagination via MORE prompts as needed, long topics can avoid
taking over the main game window. This is nice because it leaves the
original game window intact (without any need to clear the screen) after
the user exits from the menu system.
The MenuItem class has a new property, heading, that specifies the
text to display as the heading of the menu while the menu is active.
By default, the heading is the same as the title, which is the string
displayed in the parent menu's list of items. The separate property
allows the caption shown while the menu is active to differ from its
title, if this is desired.
A couple of the English messages for the npcMessagesDirect group have
been recast as quoted statements from the NPC, for consistency with
other messages in the group. The affected messages are
noMatchDisambig and disambigOrdinalOutOfRange. In addition,
askDisambig (in the same group) has been tweaked slightly to
accommodate this change.
The CollectiveGroup class has been split into two classes. The full
behavior of the old class is now contained in the class
ItemizingCollectiveGroup, which is a subclass of CollectiveGroup. On
Examine, the CollectiveGroup class no longer shows the itemized list
of collected items, but instead simply shows its own description using
the normal Examine handling. The itemizing behavior is desirable in
some cases of collective groups, but certainly not all; this change
makes it easy for the game to select whether or not the itemizing
behavior is used.
If a check() routine (in a dobjFor() or iobjFor() group) terminates
the command with 'exit', the parser now automatically marks the action
in the transcript as having failed. This is the same thing that the
reportFailure() macro does, so this means that it's never necessary
to use reportFailure() from within a check() handler. Since the purpose
of check() is to enforce conditions on the action, exiting from the
check() routine necessarily means that the command has failed; this
change takes advantage of that to save games a bit of work.
The new Thing property isKnown lets you specify that an object is
known in advance to the actors in the game. The method isKnownBy(actor)
can be used to test actor knowledge of a Thing: isKnownBy(actor) returns
true if seenBy(actor) returns true, or the isKnown property is set to
true for the object. The Actor methods knowsTopic() and isLikelyTopic()
now use isKnownBy() rather than seenBy() to determine actor knowledge.
A few new conversation-related verbs have been added: Goodbye,
Yes, and No. These are handled similarly to Hello.
A new Action method, isConversational(issuingActor), determines if an
action is "conversational." A conversational action is one that
involves the issuing actor saying something, within the game context,
to the target actor, as opposed to an order to the target actor to do
something. Of the system-defined verbs, Hello, Goodbye, Yes, No, and
TellAbout (with the issuing actor as the direct object) are defined
as conversational.
The TextList subclass formerly known as SlaveTextList has been
renamed to SyncTextList, to better reflect that this list is always
kept synchronized with its associated master list. The old name
suggested that the connection was one-way, that state changes flowed
only from the master to the slave, when in fact the master and slave
are fully synchronized. The new name is more suggestive of this
two-way connection.
A new TextList subclass, StopTextList, provides a minor variation on
the standard text list script. Once StopTextList reaches its last
message, it will simply stay at the last message forever, repeating
the last message each time the script is invoked. This is useful for
cases such as a conversation topic where an actor has several things
to say, but once the actor has said each bit, the actor will from
that point on just repeat the last message; the last message would
usually be something like "I've already told you all I know about
it," or could be a summary of what the actor revealed.
The special description list order has been modified slightly. If two
objects have the same specialDescOrder value, but one of the two
objects is inside the other, we'll list the outer object first. So,
specialDescOrder still dominates, but when there's no specialDescOrder
preference, we'll list containers before their children. In almost
all cases, this produces a more pleasing list order; objects within
other objects will frequently mention their placement in their special
description text, so it's usually better to have seen the containing
item's own special description before we see the containing item
mentioned in a child item's special description.
The new class LocateInParent makes it easy to define a nested object
that's to be located within the enclosing object. This is a mix-in
superclass, so simply add it to the object's superclass list;
LocateInParent should go ahead of Thing or any Thing subclass in the
superclass list.
The menu system has a few minor changes to make the user interface
more easily customizable.
- In HTML interpreters, the top title/instructions bar now shows
a hyperlink that returns to the parent menu. The text in this
hyperlink is controlled by the 'prevMenuLink' property, which
by default uses the text 'Previous'.
- In HTML interpreters, a hyperlink is shown in topic lists to
advance to the next topic item. The text of the hyperlink is given
by the 'nextMenuTopicLink' property.
- The 'fgcolor' and 'bgcolor' properties now look to the parent
menu, if there is a parent menu, for their default values. For
the top-level menu, the defaults are still the status line colors.
In most cases, you'll want all of the menus in an entire menu tree
to have the same appearance, and this change makes it easy to
accomplish this: simply specify the appearance in the top-level
menu, and all of the child menus will use the parent menu settings.
Of course, individual menus can sever this parental dependence simply
by overriding these properties.
- Two new properties, 'topbarfg' and 'topbarbg', allow each menu to
specify the foreground (text) and background colors of the top
title and instructions bar. By default, these look to the parent
menu, if there is one; the top-level menu defaults to using the
inverse of the color scheme of the menu itself.
- The 'indent' property uses the parent's value by default.
- A new property, 'fullScreenMode', lets you indicate that you want
the menu to take over the entire interpreter window. By default,
menus are given just enough space at the top of the interpreter
window to display their contents. If 'fullScreenMode' is true,
though, menus will cover the entire interpreter window. Full-screen
mode has the advantage that it's less jumpy than the default
partial-screen mode, since the partial mode resizes the menu windows
on each navigation operation to accommodate the new contents.
A new hint menu type has been added: HintLongTopicItem. This is
simply a MenuLongTopicItem subclass designed for use in hint menus.
(Regular HintLongTopicItem objects don't have the necessary logic for
calculating visibility in a hint menu, so the specialized subclass
should be used for long-topic menus within hint menu trees rather
than the base MenuLongTopicItem type.)
The library's turn-counter incorrectly counted a command as two turns
if the command's action() handler invoked a nested action which was
then remapped (via remapTo). Nested actions aren't supposed to count
as separate turns; it was erroneous for the remapping to affect this
one way or the other. This has been corrected.
moveInto(nil) now works properly for a MultiLoc object. (In the past,
this incorrectly caused a run-time error.)
SensoryEmanation has a new property, isAmbient. This is nil by default;
if set to true, it indicates that the noise/odor/etc is purely in
the background, so it's not especially noticeable. Ambient emanations
won't be mentioned when they first become sensed; normally, an emanation
is automatically mentioned whenever conditions change so that it was
not sensed previously but is now. Ambient emanations will still be
mentioned in explicit intransitive LISTEN/SMELL commands; they simply
won't be mentioned on their own.
Part of the Container class has been separated into a new lower-level
base class, Encloser. An Encloser is an object that can enclose its
contents, so that all senses must pass through the encloser's
material when passing in and out of the object. Encloser has all
of the basic handling for enclosing contents, but not the action
handling. Encloser is meant for cases where the object's contents
are not to be directly manipulable by the player, via "put in"
commands and the like. Container is now a subclass of Encloser;
Container defines the suitable action handlers to allow a player
to manipulate the container's contents.
A new mechanism in the Actor class makes it relatively easy to set it
up so one or more non-player characters accompany the player character
on travel. This kind of group travel is especially good for things
like a sidekick character who goes everywhere the player does, and for
tour guides or other escorts who are showing the player character
where to go.
This mechanism is similar to the "follow" mechanism, which makes
one actor attempt to follow another on each turn, but it improves
considerably on regular following by customizing the messages. Normal
following is a little awkward for explicit group travel of the sort
that one wants with sidekicks and escorts, because the messages are so
generic; the NPC almost seems to be wandering around on its own and
just coincidentally showing up where the PC is. This new
"accompanying" mechanism accomplishes much the same thing, but smooths
out the messages a bit. First, rather than having the NPC trailing
along after the fact, the new system sends accompanying NPC's on
ahead; this means that the NPC's don't just wander in later as they do
with normal following. Second, the message for the NPC's
pre-departure is customized to make it clear that the NPC isn't
departing, but is coming along with you. Third, on arriving in the
new location, there's a customization hook for describing the NPC's
presence in the new location specially for the group travel; this is
important because it gives us a place to mention that the actor starts
doing whatever it is the actor normally does in the new location. The
overall effect is that each group travel action is made to look like a
single, integrated action, rather than two generic and unrelated actor
travel actions.
NPC's can also be set to accompany NPC's with the same mechanism,
but it's much less interesting for that, since the main value of the
new mechanism is that it improves the messages for PC-plus-NPC group
travel. For NPC-plus-NPC group travel, this new mechanism has no
particular advantage over the the simpler "follow" mechanism.
Setting up accompanying travel is relatively straightforward.
You have to specify the conditions under which the group travel occurs,
and you should customize two messages related to the travel. You
set this all up by overriding methods on the NPC who's going to follow
the lead actor (usually the PC). The methods to override are as
follows.
First, override the accompanyTravel() method so that it returns
true under the conditions where you want the accompanying travel to
occur. This method is called each time your NPC is present and sees
another actor attempt a travel action (it's called during your NPC's
beforeAction when the action is a TravelVia). If you want your actor
to accompany the PC everywhere, simply return true when the action
actor is the PC. The method also has access to the TravelConnector
involved in the travel, so you selectively accompany an actor for
some destinations but not others, if you wish.
Warning: the next two parts are likely to be modified soon.
I'd recommend against writing any code that makes customizations using
these features right now.
Second, define an accompanyDesc method for your actor. This method
is used instead of the usual actorHereDesc method to describe your
actor in the new location immediately after the group travel is
finished. It's usually desirable to use a special message to describe
the actor right after the accompanying travel, because you usually
want to convey that the actor is just arriving - not that the actor is
walking in separately from the lead actor, but that the actor is
arriving at the same time as the lead actor. If you do override
accompanyingDesc, you should override accompanyingListWith to return
an empty list ([]).
Third, you can optionally provide a special TravelMessageHandler
for your actor by overriding getAccompanyingTravelMessageHandler. By
default, this routine provides a message handler that uses messages
like "Bob goes with you" instead of the usual "Bob leaves to the east"
to describe the departure of your actor. The normal departure message
isn't appropriate, because the actor isn't just leaving, it's leaving
*with* the lead actor. The default "Bob goes with you" is better, but
you might still want to override this handler to provide even more
customized messages: "Bob escorts you to the east," or "You drag Bob
with you," or whatever makes sense for the specific situation.
Finally, note that you can use accompanying travel and the regular
following mechanism together. Regular following can be a useful
fallback for cases you don't want to customize. Regular following
occurs after the fact, because it occurs on an NPC's turn when the
NPC sees that the actor it's tasked to follow is no longer present.
Accompanying travel, in contrast, happens on the same turn as the
lead actor's travel. This means that regular following is essentially
overridden by accompanying travel, since it happens first.
The English parser now accepts quoted strings as adjectives. Quoted
strings can be used as adjectives in exactly the same way that numbers
can be used, so phrases such as "QU" TILE and BUTTON "G" are accepted.
For the purposes of matching vocabulary in the dictionary, quoted
adjectives are simply treated as though the quotes weren't present.
So, "QU" TILE is treated exactly the same as QU TILE. This means you
don't have to worry about the quotes when defining your vocabulary
words.
There is an additional bit of special treatment for quoted strings.
The special vocabulary word '"*"' (that is, an asterisk within
double-quote characters) serves as a wildcard for any quoted string.
If an object defines this special wildcard as an adjective among its
vocabulary words, then that object will match any quoted string
as an adjective. This is the equivalent of the '#' wildcard string
for numeric adjectives.
String-as-adjective phrasing is implemented using the new
production literalAdjPhrase. This new production is now used
wherever numberPhrase was formerly used in the role of an adjective
in noun phrase rules. literalAdjPhrase matches numbers, '#' number
phrases, and quoted strings.
In travel.t, there were formerly a couple of TravelConnector
subclasses that handled action synonyms for TravelVia using
asDobjFor(TravelVia). These have been changed to use
remapTo(TravelVia) instead.
The asDobjFor() approach had the drawback that the action
synonyms didn't ever generate TravelVia actions proper, but
simply called the TravelVia handlers internally; so, for example,
beforeAction() routines wouldn't ever see a TravelVia action
being performed. The new remapping approach is better because
it means that every possible action on these objects that
involves travel will go through an actual TravelVia action.
This allows beforeAction() and similar routines to be assured
that they can catch all travel actions by looking for TravelVia
alone, eliminating the need to worry about synonym actions that
could cause travel.
A couple of new classes simplify a few tasks involving entry
and exit portals. Exitable is a new class that's similar to
Enterable, but provides Exit instead of Enter as its main
action. EntryPortal and ExitPortal are just like Enterable and
Exitable, respectively, but add support for GoThrough actions.
Added finishOptionFullScore, to make it easy for the game to
offer the player the option of seeing the full score at completion.
Released March 23, 2003
It's now less dire for gPlayer.location to be set to nil. In the
past, setting gPlayer.location to nil caused an infinite error loop,
because the status line code dereferenced gPlayer.location without
checking for nil, which caused an error, which aborted the current
turn, which started a new turn, which tried to update the status
line, and around we went. It's still not completely valid for
gPlayer.location to be nil; this change just removes the infinite
error loop, which makes it a little easier to deal with this problem
arising during programming and testing.
Changed Thing.dobjFor(Remove) to remap to RemoveFrom (asking for an
iobj). Wearable now overrides Remove to map it to Doff. It makes
more sense in general for "remove foo" to mean "remove foo (from
something)" rather than "doff foo"; only when "foo" is an article of
clothing does the "doff" interpretation usually apply.
Added the new TravelConnector property isConnectorListed. If this
property is nil, the connector is suppressed from generated exit
lists (such as the status line exit list, and "you can't go that way"
messages).
Added a new class, UnlistedProxyConnector, that acts as a proxy for
an underlying connector, but is unlisted. The idea is to make it
easy to add an exit that acts exactly like another connector, but is
suppressed from exit lists.
Added a macro, asExit(), that can be used to define an
UnlistedProxyConnector that links to another direction exit.
Integrated Stephen Granade's menu system (menus.t) with the main
library.
Added an on-line adaptive hint system (hints.t).
Added the new class RestrictedContainer. This is a Container
specialization that makes it easy to define containers that only
accept certain objects as contents. In the basic implementation, the
allowable objects are simply enumerated as a list of objects; but
this can be easily overridden for other methods of determining which
objects are allowed.
Added the new class SingleContainer, and the associated new
precondition objEmpty. SingleContainer is a specialization of
Container that only allows a single object to be in the container at
a time; this is suitable for things like sockets and receptacles.
SingleContainer places the objEmpty precondition on the PutIn action;
the objEmpty precondition simply requires that the subject object is
empty, and tries implicit TakeFrom actions on the contents to
accomplish this.
In en_us.t, changed typographicalOutputFilter.eosPattern to ignore
any run of HTML tags between sentence-ending punctuation and a
following space.
The main implicit action processor now uses the more abstract
"allowed in implicit" test on the verify results, rather than merely
checking for "dangerous" results. This will allow results flagged as
"non-obvious" to cancel implied actions, as they should. (A
non-obvious command should never be done implicitly, because implicit
commands are specifically for actions that are obvious intermediate
steps toward the explicitly stated command.)
Openable and Lockable have been cleaned up slightly. A new class,
Linkable, has been added for objects that can be paired in
master/slave relationships; Openable and Lockable are now based on
Linkable. This removes some duplicated code for managing the
masterObject relationship. In initialization, Linkable checks for a
master object loop (where each object points to the other as the
master) and break such loops by arbitrarily selecting one as the
master. Also, we have reduced the number of times we have to refer
to masterObject by relying on isOpen/isLocked/makeOpen/makeLocked to
defer to the master. Also, the initiallyOpen/initiallyLocked
initializations are now handled in initializeThing() rather than in
isOpen/isLocked.
Another new class, BasicOpenable, provides the basic state
management for openable objects (which can optionally be linked
in pairs to maintain the same status across the pair) but doesn't
provide any of the verb handling of Openable. This can be used
for objects that want to maintain open/closed state using the
usual method names, but which don't respond to direct player
open/close commands.
IMPORTANT: Objects based on Openable and Lockable must
not initialize their open status with isOpen or their locked
status with isLocked. Instead, initialize the status with
initiallyOpen and initiallyLocked, respectively. Also, be sure
you never set isOpen or isLocked directly; instead, call makeOpen()
and makeLocked() to effect these status changes.
Changed Passage slightly to rely on isOpen/etc more to manage the
masterObject relationship.
Added a parameter to Direction.defaultConnector() giving the location
from which travel is being attempted. This allows the direction to
customize the default connector according to the type of location.
Added a new property, isShipboard, to Thing. By default, if we have
a location, we use the location's isShipboard setting, otherwise we
return nil. Rooms aboard ships can set this to true to indicate that
shipboard directions make sense here.
In ShipboardDirection.defaultConnector, if the source location or
any container has 'isShipboard' set to true, then we now use noTravel
as the default connector rather than noShipTravel, as the latter is
meant to convey that shipboard directions make no sense in
non-shipboard locations.
Added a ShipboardRoom mix-in class that defines isShipboard to true.
Changed the way that VerifyResultList determines if an action is
allowed at all or allowed as an implicit command. In the past, this
calculation did what the "most disapproving" result in the list said
to do; this wasn't quite right, because approval is essentially a
separate axis from the "disapproval" order, despite the name. In
reality, the "disapproval" order is the message priority order, and
the actual determination of approval depends on there being
no disapprovers. So, to determine approval, we now scan the full
result list and require that every result approves; a single
disapproval, no matter where it appears in message priority order,
constitutes disapproval.
In the English module, added a new token type for numbers specified
with a leading pound sign (as in "#2 pencil" or "room #101"). These
are treated essentially the same as numeric adjectives. These match
vocabulary for just the underlying numbers, not including the pound
signs, so vocabulary should be specified simply as '2 pencil' and
'101 room'. (In other words, do not include a pound sign in
vocabulary words; just use the number as with ordinary numeric
adjectives, and the parser will match the number with or without the
pound sign).
The TravelVia check() condition in Passage no longer uses isOpen as
its test. Instead, it calls the new abstract condition method,
canActorTravel(), passing the current actor as a parameter. This
makes it easy to allow some actors to pass and not others, and to
test for conditions other than isOpen. By default, canActorTravel()
simply returns isOpen, so this doesn't change the default behavior.
Along the same lines, Door.connectorTravelPreCond() now calls
a separate method, getDoorOpenPreCond(), to obtain the door-is-open
precondition object. By default, this returns a doorOpen precondition
(wrapped with an ObjectPreCondition for 'self'), so the default behavior
hasn't changed.
In the past, when an NPC was following another actor, and the actor
being followed moved into a nested room within the current room, we
generated a redundant message:
>stand on platform
Okay, you are now standing on the platform.
Bill stands on the platform.
Bill follows you onto the platform.
The redundant "Bill follows you" has been eliminated.
The reporting mechanism now processes sets of implicit action announcements
to make them more readable and more easily understood. In the past,
each implicit action was shown separately; when one implicit action
triggered another, this could make for somewhat confusing displays.
For example, in the Platform Room in sample.t, if the PC is sitting on
the blue chair and wants to get up and sit on the red chair, we formerly
generated a transcript like this:
>sit on red chair
(first standing on the red platform)
(first getting off of the blue platform)
(first standing)
Okay, you're now sitting on the red chair.
To the uninitiated, this looks backwards: shouldn't we stand up,
then get off the blue platform, then get on the red platform, and then
sit on the red chair? Of course, that's what's actually happening,
and in its own way the transcript above reflects this: the second
"first" applies to the first "first": it's saying "before you can
stand on the red platform, you first have to get off the blue
platform." Likewise, the third "first" applies to the second "first,"
to say "before you can stand on the red platform, you have to stand up
first." In other words, the implied reports are nested recursively.
The recursive structure isn't represented visually, though, so it's
not evident whether one of the later "firsts" refers to the preceding
"first" or back to the original command line. It would have been
possible to represent the recursive structure visually, using
indentation or something like that, but this probably wouldn't have
made most people very happy; most non-programmers aren't accustomed to
thinking in terms of recursion and stacks and tree views, and even
programmers might well have found this kind of presentation to be too
obviously mechanistic.
To improve the situation, the transcript processor now features a
new transform, called implicitGroupTransform, that rearranges these
recursive listings into an order that should be entirely
straightforward to anyone, programmer or not. The new transform also
consolidates these lists into a much more concise and readable format.
The transformer does two things. First, it "unstacks" recursive lists
of implied action announcements to put them into the chronological
order in which the commands were actually performed; the transcript
has always kept track internally of which announcement is tied to
which action, so the new transformer can merely inspect these records
to determine the action relationships and use this information to
unwind the stack. Second, the transformer combines each run of
adjacent implied action announcements into a single announcement,
showing a list of the actions performed. The result is that the
example above now looks like this:
>sit on red chair
(first standing, getting off of the blue platform, then standing on
the red platform)
Okay, you're now sitting on the red chair.
This new format should be easier for players to understand,
since it shows implied actions in the order in which they're actually
carried out, eliminating any need to be aware of the recursive
goal-seeking work that the system is doing internally. Hopefully,
players will also find it more readable and less obviously mechanistic
than the old format.
3.0.6e was released March 16, 2003
For a restore-on-startup operation, if the restore fails, don't simply
start the game from the beginning. Instead, offer options to START
from the beginning, RESTORE another game, or QUIT, using the same type
of prompt that we use for presenting "finishing" options.
Add a verb for OOPS typed at arbitrary times, which simply explains
that OOPS can only be used after the parser has pointed out an unknown
word.
Fix problem using possessive qualifiers in topic phrases. (The
problem is that we're using the topic resolver to resolve qualifiers
in the topic phrase; we should be using an ordinary object resolver.
Change the topic resolver's qualifier resolver to use the topic
action's direct object resolver by default.)
Move the pronoun-setting routines (setPronoun, setIt, setHim, setHer,
setThem) into Actor - these shouldn't be global functions since they
need to operate on the current actor.
In TopicAction, set the pronoun antecedent based on the direct object
phrase immediately during resolution, to allow things like "ask bob
about his book."
Add a DefineIAction macro, for defining IAction classes.
Base LiteralAction on IAction, not directly on Action.
Because of this change, rewrite OopsAction to implement its processing
in execAction() rather than doActionMain().
Refactor the Instructions action into an InstructionsAction base class
and a separate grammar rule, to allow the base class to be referenced
by name from other code.
Add a banner manager, along the lines of the input and output
managers. A new BannerWindow class would represent a banner; this
would encapsulate the system banner handle and provide methods that
operate on the banner. The game should use the BannerWindow methods
to perform all operations on banners, rather than calling the
system-level banner API directly.
The banner manager should provide persistence support, so that the
on-screen banner layout is restored on RESTORE, UNDO, or RESTART. To
this end, BannerWindow should be an ordinary persistent object, and a
separate set of transient objects should track the current UI state.
On RESTORE, UNDO, or RESTART, the transient tracking list and the
persistent BannerWindow states should be compared, and the actual
on-screen UI state, as represented in the transient objects, should be
brought into line with the saved state.
Wait to mark an object as 'seen' in Thing.lookAroundWithin() until
just before the method returns, so that any routines called from the
method can check the old 'seen' status.
Fix gAction reference in Actor.travelWithin() so that it's conditional
on gAction being non-nil.
Fix takeFromNotInActor message (iobj/dobj are reversed).
Fix TAction.retryWithMissingDobj() and
TIAction.retryWithMissingIobj() so that they cancel the game time
contribution of their enclosing (replaced) actions.
Fix touchObj precondition logic so that it realizes when an attempt
to remove an obstruction with a precondition fails. To do this, keep
track of which obstructions we've tried to remove with implicit
commands, and give up if we encounter the same obstruction more than
once.
Add <.parser> to msg_neu.t responses for some system verbs (NOTIFY
ON/OFF, EXITS ON/OFF, a few others).
Assign the IndirectObject role to the literal in a LiteralTAction by
overriding getRoleFromIndex, getObjectForRole, and getMatchForRole.
These are needed to allow remapTo to be used with a LiteralTAction.
Note that the literal is always in the IndirectObject role,
regardless of whichLiteral, which only specifies the grammatical role
for message generation purposes.
Likewise, assign the IndirectObject role to the topic in TopicAction.
This will allow remapTo to be used with a TopicAction.
Rename TopicAction to TopicTAction, parallel to LiteralTAction.
Add a new TopicAction that takes only a topic as its object, parallel
to LiteralAction.
Rename whichObject, whichLiteral, and whichTopic to
whichMessageObject, whichMessageLiteral, and whichMessageTopic,
respectively, to make it clearer that these apply only to the roles
played in generated messages, and not to any other roles.
In particular, the message role only affects the way the object is
used in generating messages based on the verb, such as "what do you
want to open it with?".
It would be better to interpret "type on typewriter" as having a
missing literal than as having a missing ON phrase (in other words,
we want to interpreter this as TYPE <empty literal> ON TYPEWRITER
rather than as TYPE "ON TYPEWRITER" <on missing object>).
To do this, add a verb rule for TYPE ON <object>, without any literal
phrase. For most objects, fail in verification; but when
verification passes, in the action, ask for a missing literal phrase.
(This has the additional benefit that it makes it easy to create
objects that allow generic typing on them, as in TYPE ON COMPUTER,
for situations where the game doesn't want to make the player type
anything specific but still wants to allow the generic act of typing
on the object.)
>ASK BOB ABOUT
what do you want to ask him about?
>BILL
"Ah, yes, , very interesting..."
(The problem is that we're not propagating getOrigText() from the
empty topic phrase match down to the underlying replacement match.)
>ASK BOB
what do you want to ask him about?
>ABOUT BILL
"Ah, yes, about bill, very interesting..."
(The problem is that we need an aboutTopicPhrase production to parse
responses to ABOUT WHAT questions.)
Add a generic Settable class for things (such as dials) that can be
set to various settings. Base Dial on Settable.
Do not override lookAroundWithinDesc() in BasicLocation; instead,
provide an implementation of BasicLocation.roomDesc that simply uses
the 'desc' property to display the room's interior description.
(This allows nested rooms to differentiate more easily between
interior and exterior descriptions, if they want to.)
Add a new method to Thing, filterResolveList(), to allow "global"
filtering of a noun phrase resolution list. Call this method from
the parser where we use 'verify' to filter a resolution list. Unlike
'verify', this new method has access to the entire resolution list;
this allows the method to take action based on which other objects
are in the resolve list.
Add a new mix-in class, Collective, to serve the purpose of the
former isCollectiveFor(obj) method in Thing. To create an object
like the matchbook in the sample game, use Collective as an
additional base class.
Remove the special-cased plural filtering in AllPluralProd and
DefinitePluralProd. Move this logic instead into Collective's
filterResolveList() implementation.
Add a new class, CollectiveGroup, to allow creating "abstract"
collective objects. These differ from regular Collective objects in
that a Collective is actually a simulation object (such as a
matchbook that can hold several matches), whereas a CollectiveGroup
is not a separate object from the player's perspective (so it's never
listed as a separate object in a room's contents listing, for
example).
Add a new Thing property, collectiveGroup, that allows an ordinary
object to be associated with a CollectiveGroup object. In
CollectiveGroup's filterResolveList() method, choose to keep either
the individuals (the ordinary Thing objects associated via their
collectiveGroup properties with a CollectiveGroup object) or the
CollectiveGroup in the resolution list, but not both. By default,
make the selection based on action; subclasses can override as
desired to use other criteria.
CollectiveGroup can be used to create things like a "money" object
to represent a set of coins and bills, so that a command like "look
at money" can respond with a single description of all of the money
present, rather than iterating over all of the coins and bills
individually.
Eliminate the actorHereLister. Instead, handle actor descriptions
using the special description mechanism. By default, give actors
a higher specialDescOrder, to keep actor special descriptions listed
after other special descriptions.
Add a new special description control property to Thing,
specialDescBeforeContents; set it to true by default in Thing, but
override it to nil in Actor. In verbose room descriptions, show
special descriptions in two phases: where we currently show special
descriptions, just before the room's list of portable contents, show
special descriptions only for items with specialDescBeforeContents
set to true. Then, after we've shown the portable contents list
and any other sensory messages (listen/smell), show the special
descriptions for objects with specialDescBeforeContents set to nil.
This will allow the traditional ordering, with actor "I am here"
messages placed after the rest of the room description, while keeping
the rest of the special descriptions grouped with the room's main
description. Most special descriptions belong with the main room
description because they're meant to be extensions of the room
description. Some special descriptions are not; they're meant to
describe more ephemeral status information, so work better when
grouped with the other status-like messages. Actors in particular
fall into the latter category, since actors are meant to seem
autonomous, not parts of the rooms they occupy.
When showing special descriptions of the contents of an object as part
of the object's description, use a new form of the special description
method, showSpecialDescInContentsWithInfo(), and corresponding
specific methods for viewing conditions (showSpecialDescInContents,
showObscuredSpecialDescInContents, showDistantSpecialDescInContents).
Override showSpecialDescInContents in Actor, so that we can show
the type of description we previously showed using nestedActorLister.
Remove nestedActorLister and NestedRoom.examineNestedRoomActors().
Add a default output filter, typographicalOutputFilter, to the main
output stream. In this filter, convert '--' and '---' sequences to
typographical dashes, and insert an en space at the end of each
sentence.
The conversion of sentence-ending punctuation compensates for the
elimination of the double-space insertion in the VM-level formatter.
It also improves matters over the old way by making the treatment of
sentence-ending punctuation customizable: the game can replace the
filter method with its own custom conversions. This allows
customization not only for stylistic variation but for
language-specific conventions as well.
Add the special {subj} messageBuilder token. This token simply notes
its target object as the subject of the sentence, for internal
book-keeping purposes, but doesn't actually display anything.
Add a "log file console" type. This would act like the main console
or a banner console, in that it would apply the full set of VM-level
output formatting (including text-only HTML interpretation) to the
text sent through the stream, but it would write the output only to a
file, rather than displaying it anywhere. This would be useful for
capturing output to a file without showing the output at all on the
display; for example, this would make it easy to generate an About.txt
file by capturing the output of the ABOUT command to a file during
preinit, without having the ABOUT output also show up on the screen.
This would require a new set of VM-level functions in the tads-io set:
- logConsoleCreate(filename, charmap, width):
returns a handle to the new console
- logConsoleSay(handle, ...): writes the arguments to the
log console; works like say() and bannerSay()
- logConsoleClose(handle): closes the console
In addition, add a LogConsole library class to simplify operations
with the log console. This class can be quite simple; it's just an
OutputStream subclass that implements the writeFromStream method to
call logConsoleSay().
Add a TopicQualifierResolver subclass of Resolver, specifically for
resolving qualifier phrases in topics. Use a new instance of this
class instead of the direct object resolver in TopicActionBase. (The
direct object resolver isn't really appropriate, and doesn't work at
all with plain TopicActions, because they don't have direct object
resolvers at all.)
Add a new mapping macro, iobjAsDobjFor(), that makes it safe to map
an indirect object handler to a direct object handler in the same
object. Use this for the mapPushTravelHandlers() macro.
(The mapPushTravelHandlers() macro essentially wants to decompose the
two-object action into two single-object actions, which isn't safe
with the regular asDobjFor() mapping. The difference with
iobjAsDobjFor() is that this new routine temporarily makes the
indirect object take on the direct object role, so that the target
dobj handler sees the proper object in the direct object slot.)
Add a LabeledDial subclass of Dial. This class accepts arbitrary text
labels as dial stops; the property validSettings contains a list of
strings giving the valid dial stop labels.
Remove any .t and .tl extensions from adv3.tl and en_us.tl. (The
extensions are implied by the file types; it makes the library file
more portable to omit the extensions, since the compiler will
automatically apply the default extensions using the appropriate local
conventions when the extensions aren't explicitly included in the
names as they appear in the library files.)
Get rid of Thing.articleIndef; games should simply override aName
instead when they want to override the default indefinite article
determination.
Change the way theDisambigName, aDisambigName, and countDisambigName
work. If name == disambigName, then return the corresponding xxxName
from xxxDisambigName; otherwise, apply the same algorithm to
disambigName that the corresponding xxxName applies to name. For
example, in aDisambigName, if name == disambigName, simply return
aName; otherwise, apply the indefinite article algorithm to
disambigName and return the result.
This change will allow disambigName to be overridden without
requiring all of the xxxDisambigName's to be overridden at the same
time, because the xxxDisambigName's will by default apply the
standard algorithms to the modified disambigName. At the same time,
though, this has the virtue of the original implementation that, in
the common case where disambigName is not overridden, any overrides
to theName, aName, and countName will be used for the corresponding
xxxDisambigName's. Furthermore, since these are all still separate
methods, objects can still separately override each xxxDisambigName
as needed for cases where the disambigName is customized and the
customized name requires overriding the normal article algorithms.
While we're at it, add pluralDisambigName, using the same logic.
Remove the dictionary properties possessiveAdj, possessiveNoun, and
their corresponding grammar. (These are no longer needed, since we
now have explicit grammar for all of the possessive pronouns in
adjective and noun usages. At one time, these were used for adding
vocabulary for the player character, but it worked better to use the
grammar and resolver mechanism more explicitly.)
Rework "'s" handling for literal vocabulary:
First, remove the special-case tokenizer handling for "'s" words that
appear in the dictionary, so that we handle all "'s" words
uniformly in the tokenizer: all "'s" suffixes are treated as separate
tokens.
Second, never add "'s" words to the dictionary in initializeVocab().
Instead, define a new dictionary property, adjApostS; when we see a
"'s" word in a vocabulary list, remove the "'s" suffix and add only
the prefix to the dictionary, but add it as an adjApostS instead of
as an adjective. (For simplicity, don't bother with nounApostS at
all; allow only adjectives as apostrophe-s words.)
Third, add a new grammar production, adjWord, that accepts either a
single adjective or an adjApostS followed by an apostrophe-s token.
Fourth, where appropriate, change 'adjective->' grammar rules to use
'adjWord->' instead.
These changes correct the problem that adding a literal "'s" word to
an object's vocabulary prevented that word from being used as a true
possessive phrase in the grammar. The old tokenizer rule was that a
word that appeared with an explicit "'s" suffix in the dictionary was
kept intact, rather than split into separate tokens for the root word
and the "'s" suffix. Since the word was kept intact as a single
token, it couldn't match the grammar rules for possessive phrases,
which only match the separated form. These changes allow the same
word to be used in either context, since everything is treated the
same way in the tokenizer; even if a word is used as a literal
vocabulary word with a "'s", it'll still be split up during
tokenization. Even with this change, we can still match explicit
"'s" vocabulary words, thanks to the adjWord grammar.
Note this has one small potential impact on existing games: if an
object explicitly defines its own 'adjective' property (rather than
using the automatic library initialization), and a word defined in the
adjective list ends in apostrophe-s, then that word must be stripped
of the apostrophe-s suffix, removed from the 'adjective' list, and
added to a new 'adjApostS' list instead. Similarly, if any vocabulary
words that end in apostrophe-s are dynamically added with
G_dict.addWord(), the apostrophe-s words should be stripped of the
suffix and added under the '&adjApostS' property instead of the
'&adjective' property.
Add noteLiteral(lst_.getOrigText()) to getVocabMatchList() in
simpleNounPhrase(misc)? This would make the length of text in a
miscellaneous word list a match selection criterion (less important
than the presence of a misc word list). This won't change anything in
cases where we want to choose between a match with a misc word list
and one without, as the presence or absence of a misc word list is
more important than literal length, but it will help distinguish
between two matches that both have misc word lists. Since we prefer a
shorter literal match, this will have the effect of preferring to
match the more structured interpretation, since a shorter misc word
match means a longer grammar match.
Fix run-time error with "a
" (the 'inherited' argument list
is wrong in AskAboutAction.getDefaultDobj; the same problem is in
TellAboutAction).
Rename class Fixed to Fixture.
Add new class Immovable: this is for objects that aren't fixed in
place obviously or by their very nature (as are Fixtures), but rather
are fixed for some other reason: great weight, hidden fasteners, etc.
This class differs from Fixture in that it disallows actions like
take, move, push, and pull not in the verify() but in the action().
Add a subclass, Heavy, for objects that are immovable because they're
very heavy. This is suitable for things like large furniture and big
boulders.
Remove moveInto() override on Immovable. (This attempted to be a last
resort to disallow actions that involved trying to move the object,
but it prevented programmatic relocation as well, creating more
problems than it solved.)
Fix problem with applying AGAIN to a command issued to another actor:
the target actor becomes "busy" and won't accept another command until
a turn passes. The problem is that we're not accounting for
synchronous actors in the AGAIN processing: we need to tell the issuer
to waitForIssuedCommand on the target actor.
Fix problem in GiveTo and ShowTo actions: getDefaultIobj calls
inherited() with wrong argument list.
Fix the parameter list for npcActionMessages.okayTurnTo.
Make RandomTextList customizable with the percentage of the time a
message is generated at all. Random message lists are often used for
atmospheric messages, and it's usually better if these messages don't
appear on every single turn. Add a 'messagePercent' property that
determines the percentage of turns where a message is generated; the
default is 100, which leaves the default behavior as it was (i.e., a
message is generated on every turn).
In addition, especially with random atmospheric messages, it can get
tedious to see the same messages over and over if you spend a lot of
time in the same area. It's therefore often the case that we want
frequent atmospheric messages when the player first gets to a
location, but then we want the frequency to drop dramatically after
the player has spent more than a few turns there. To make this easy
to handle, add a couple of new properties: 'messageReduceAfter' is the
number of times that we want to see messages at the initial frequency,
and 'messageReduceTo' is a new value for 'messagePercent' that we'll
apply after we've generated the messages 'messageReduceAfter' times.
Make these nil by default, which means that there is never a reduction
in the frequency. Authors can use these properties to generate random
atmosphere messages at high frequency at first, but then drop the
frequency to just an occasional message after the player has been in
the location long enough to get the idea.
Add a new class, NominalPlatform. This is a "secret" object: it's not
visible to the player as a separate simulation object, and won't
normally have any vocabulary or appear in any room listings. The
purpose of this object is to make it easier to describe NPC's as
standing somewhere specific within a room; the author locates an NPC
within the NominalPlatform, and the library will automatically
describe the NPC as standing on the platform: "Bob is here, standing
in the corner." The author can also customize the display methods for
the nominal platform so that the description is something like
"leaning against the lamppost."
Add a new property to Thing, specialContentsLister, that lets
individual objects and rooms customize the lister they use to generate
special descriptions. Set the property by default to
specialDescLister.
Add a new topicPhrase grammar rule that matches a miscWordList without
badness. (The normal topicPhrase grammar matches a singleNoun, which
matches a miscWordList, but it does so with badness; we want to add an
explicit rule that matches a miscWordList without badness.) It's in
the nature of topic phrases to go outside of what's implemented in the
simulation model; we don't want to treat resolvable and irresolvable
topic phrases any differently at the grammatical level, because doing
so can lead to inconsistencies when parsing a verb containing both a
topic and another kind of noun phrase.
Add gLiteral to adv3.h, analogous to gTopic.
Change Thing.dobjFor(Attack) so that the default Attack handling is
simply to display the message "it's useless to attack that." Remove
the askIobjFor(AttackWith). Specifying a weapon shouldn't be
required; especially for attacks like "hit" and "kick," asking for an
indirect object will seem wrong to a player. This makes a tiny bit of
extra work for authors when AttackWith is overridden, since Attack
will have to be overridden as well in such cases, but this is probably
worthwhile anyway because it'll encourage authors to make the same
distinction between weapon and weaponless attacks that players are
likely to make in their own minds.
Move the code from Contents.examineContainerContents into
Thing.examineStatus (actually, into a new method called from
Thing.examineStatus, call it examineListContents). Get rid of
Surface.examineSurfaceContents. Ordinary Thing objects should
traverse into their contents during Examine; this shouldn't be limited
to Container and Surface objects. We need to do this listing in
Thing, because a Thing could contain a component that's a container,
and we want to list its contents when examining the Thing.
Fix bug in Action.getObjPreconditions: if pre is nil before appending
the catch-all list, we'll get an error. Check that pre is nil before
appending, and just use the catch-all list by itself if so.
Change the TravelConnector.describeDeparture() and describeArrival()
interfaces to add a new parameter for the traveler. Use the traveler
parameter instead of the global gActor. This makes the travel
routines more flexible, and in particular allows them to be called
from outside of commands (in daemons, for example). It also will
improve the handling for non-actor travelers (vehicles in particular).
In RoomConnector.describeDeparture/Arrival, use gPlayerChar rather
than gActor as the point-of-view object in directionForConnector().
The description is being generated for the benefit of the player, so
it should be from the PC's perspective.
Split off a new method from each of Traveler.describeDeparture and
describeArrival: describeNpcDeparture/Arrival, which is called when
an NPC (or a traveler not involving the PC) is doing the travel.
This will simplify overriding the messages for vehicles and other
special travelers when desired, since the overriding method won't
have to make all of the checks to see if it's the PC doing the travel.
Run all of the libMessages.sayDeparting/Arriving connector-specific
messages through an intermediate method call in the Traveler; these
new Traveler methods by default are simply be covers for calls to the
libMessages methods. The purpose of the extra layer of calls through
Traveler is to make it easier for individual Traveler subclasses to
customize the messages on a per-connector-subclass basis.
'@script.txt' at the command prompt should start reading from the
named script file. (This should work essentially the same way it did
in tads 2, except that we want to handle this in the library instead
of in the interpreter.) Also, '@@script.txt' should run the script in
"quiet mode," which is to say that input and output generated while
reading the script are to be suppressed.
Add some new classes to misc.t for "shuffled" random selections. A
shuffled selection is a random selection taken from a set of values
that doesn't repeat a selection until we've gone through all of the
values once, like dealing from a shuffled deck of cards.
ShuffledList: the basic shuffled selection class. Keeps a list of
values, and returns a randomly selected element on demand.
ShuffledIntegerList: a specialization of ShuffledList that returns
a randomly selected integer from a given range.
ShuffledTextList: a subclass of RandomTextList that makes its
selection using shuffling rather than independent random selection.
Add a point-of-view parameter to Thing.showSpecialDescWithInfo().
Add a parameter to the following library messages to indicate which
object is being described (the object is needed for things like gender
and number agreement in some languages):
openMsg, closedMsg, lockedMsg, unlockedMsg, onMsg, offMsg
Add a parameter to the following library messages to indicate which
actor is being described (the actor is needed for things like gender
and number agreement in some languages):
statusStanding, statusSitting, statusLying,
statusStandingOn, statusSittingOn, statusLyingOn
Add a parameter to the various name methods in ThingState (listName,
inventoryName, wornName) providing a list of the objects being shown
in this state. This is needed for some languages so that the state
description can agree (in gender and number, for example) with the
objects being desribed as being in the state.
Change the count parameter to the full list in
actorStandingGroupPrefix and the related methods in libMessages.
This will allow the prefix/suffix messages to check any attributes of
the individual objects in the list, such as gender mix, that might
affect the message.
The INSTRUCTIONS command should not take any turns (make it a
"system" command). Also, it should not consume any game time on the
real-time clock (which, given the size of the instructions, could be
noticeable).
Add an extended form of inputManager.getInputLine(), which takes a
new InputDef object to define the input parameters. Use this new
parameter definitions object to determine what to display to set and
remove the input text style; do this instead of unconditionally using
the <.inputline> style tag. The InputDef class should use
<.inputline> as the default, of course. Keep the existing
getInputLine() method, but make it a cover for a call to the new
extended version, setting up an InputDef object representing the
parameters.
Make "remove" (with a direct object but no indirect object) a fully
separate verb from "doff". In English, "remove foo" usually means
"doff foo", but it can also have the substantially separate sense of
removing a component of an object. In the English library module,
provide a default mapping of "remove" to "doff" in Thing, to keep the
default meaning the same. Making a separate Remove action will allow
this action to be independently overridden per object when it's
desirable to do so.
By default, don't list anything in the credits for a library module.
We should leave the formatting of the credits fully up to the author.
Add an optional mainRestore() function, to be provided by the game
and called from the run-time startup code when a saved game is
explicitly selected for restoration at startup (such as by
double-clicking on a saved position file from a GUI desktop, or using
the "-r" option with the command-line interpreter).
Change runGame() so that it no longer takes the initial player
character parameter. Instead, callers should always set gPlayerChar
explicitly prior to calling runGame(). (This change makes runGame()
more sensible for cases where a game is restored: since the restore
operation will restore gPlayerChar as part of restoring the rest of
the game's state, it would be redundant to pass gPlayerChar as a
parameter to runGame() only to have runGame() set gPlayerChar to that
value.)
Fix the bug in EventManager.removeMatchingEvents ('eventMatches' should
be 'cur.eventMatches')
Add a new output stream method, addOutputFilterBelow(), which adds an
output filter at a given point in the filter stack.
Remove the "\( \)" escape sequences (for highlighted text). Authors
should use HTML markups and instead.
Combine open/closed and contents status listings into a single
message, to make the openable container default description less
choppy.
Rename maxBulk to bulkCapacity, to make the meaning clearer.
Similarly, rename maxWeight to weightCapacity.
>put trike in bag. drop bag. ride trike.
(produces a run-time error)
The problem is that we're failing to treat the bag as an invalid
"staging location" on the way to boarding the trike. Add a routine,
checkStagingLocation, to Thing, and call when moving an actor into a
chosen staging location; this routine should generate an error
explaining why the travel is impossible and terminate the command
with 'exit'. BasicLocation should override this to allow being used
as a staging location.
add default Thing handling for 'feel' and 'taste' commands
>close bag; x all in bag
red ball: You cannot see that. [etc]
We're incorrectly resolving the contents of an object for an "all in
x" phrase, even when the contents can't be seen. We must filter the
resolved objects to include only the visible contents.
"all in x" doesn't filter for objects hidden from "all" phrases; it
should.
Standing should be a precondition of "jump".
The noMatchDisambig and disambigOrdinalOutOfRange messages should use
<.parser> open tags, but not close tags: instead, the <.parser> mode
should be left open, to be closed by the disambiguation re-prompt that
always follows.
askDisambig should provide an open <.parser> tag if and only if askAgain
is false: this will let the mode flow in from the preceding re-prompt
message from noMatchDisambig or disambigOrdinalOutOfRange. askDisambig
should always close the <.parser> tag.
Get rid of parserMessage(), and just use <.parser> tags instead.
We shouldn't be able to read at brightness 2. For Readable,
differentiate readability according to brightness and transparency,
as long as a readDesc is defined. (When no readDesc is defined for
the object, use the default "examine" behavior as usual.)
Disambiguation: when indistinguishables are involved, answering with
an ordinal applies the ordinal to the full list, rather than the
reduced list offered:
>take coin
Which coin do you mean, a gold coin, a silver coin, or a copper coin?
>second
Taken. [actually takes a gold coin, since more than one was present]
Add a new option flag, libGlobal.allowYouMeMixing, that controls
whether or not the parser accepts "you" and "me" in commands as
equivalent. Set this to true by default, since most games will not
have both first- and second-person narrative characters at the same
time, and hence it seems unlikely that it will ever create confusion
if the player can use "you" and "me" interchangeably. (Having the
option to make the parser strict about this will take care of any
cases where a game actually does have separate first- and
second-person characters present simultaneously.)
The forwarding scheme isn't quite right. Replace it.
First, get rid of dobjForwardTo, iobjForwardTo, remapTIAction,
dobjRemapTI, iobjRemapTI, dobjRemapTIReverse, and iobjRemapTIReverse.
Second, add a new mechanism that combines the old forwarding and
remapping schemes into a single new system. Use syntax like so:
desk: Fixed
dobjFor(Open) remapTo(Open, drawer)
dobjFor(Close) remapTo(Close, drawer)
;
The first definition above maps the Open action, when applied to the
desk, to a replacement action of Open applied to the drawer instead.
The second does the same thing for Close. This replaces the old
dobjForwardTo/iobjForwardTo scheme, so we no longer need forwarding at
all. The difference with the new remapTo scheme is that we use a
replacement action for the remapping - the remap for Open above is
essentially like using replaceAction(Open, drawer) for the action
definition of Open on the desk.
Further, consider this syntax:
lid: Fixed
dobjFor(Push) remapTo(Open, jar)
dobjFor(Pull) remapTo(Close, jar)
;
Here we've remapped not only the object (as we formerly did with
forwarding), but also the action. The first definition above says
to replace "push lid" with the new action "open jar."
Next, consider this:
ninjaThrowingStar: Weapon
iobjFor(AttackWith) remapTo(ThrowAt, self, DirectObject)
;
In this case, we're doing what the old dobjRemapTIReverse did, but
with considerably clearer syntax. We're saying that when we resolve
the indirect object of "attack with" and find that it's the throwing
star, we should remap the action to "throw at
", where is the original direct object
of the AttackWith action. The important thing about using the
"DirectObject" argument is that it tells us to use the pre-resolved
match tree for the direct object if we haven't yet resolved the direct
object noun phrase - this means that we'll be able to resolve that
noun phrase in the context of the new action (ThrowAt) rather than in
the original action (AttackWith) context.
Compiler: for each nested object definition, set a property of the
nested object, 'lexicalParent', to point to the lexically enclosing
object.
Change the default message for talking/asking/etc a random object to
indicate more specifically that it's illogical to talk to the object.
Add a class for a complex container, for objects that contain objects
in multiple ways. Examples: a crate that can contain things within
and also have objects placed on top of it; a container with
components.
The class should treat all of its immediate contents as components,
so it should be based on Thing rather than Container or Surface. It
should have two additional properties: one for a secret object that
serves as the internal container, and another for a secret object
that serves as the internal surface. Commands like "put in" and
"look in" should be redirected to the internal container, if one is
defined; "put on" and the like to the internal surface, if present.
The contents lister must specifically show the contents of the secret
internal container and surface as though they were contained directly
by the outer object.
Change default Thing handling for Push, Pull, Move, MoveWith (dobj),
and MoveTo (dobj), so that verification succeeds with a logicalRank
of 50, and report that there is no effect in the action. (These
actions all involve physical manipulations that a player might want
to try with any object, so it's better to allow these to pass
verification, so that preconditions can be allowed to proceed rather
than halting before even getting to preconditions.)
OutOfReach seems to put the object itself (not just its contents) out
of reach. Add a separate test for reaching 'self'.
In Actor.canReach, don't make touching our immediate location a special
case - just traverse the sense path to the container as normal.
>look in stove
(First opening the stove)
Opening the stove reveals a loaf of bread. The stove contains a loaf of bread.
It would be good to remove this redundancy: if we're opening
something implicitly for a look-in command, we shouldn't bother
showing what the opening reveals. (We can handle this in Openable's
Open action handler: if the action is implied, and the parent action
is LookIn with the same direct object, we can suppress the revelation
message.)
Suppress default "you see nothing special about it" or "it's an
ordinary x" descriptions when there's a special status message,
such as contents to be listed.
>look in small alcove
You see nothing in it. A trophy is visible...
(Perhaps if we special description items, and nothing listed, we should
display no message at all.)
Use tags for line-input modes in commandSequencer, eliminating method
calls. This should remove the fragile order-of-call issues that we
currently have.
Should we reformat some messages involving implied commands? For example:
>put coin in bag
(first opening the bag)
Opening the bag reveals a keyring. Done.
Perhaps we should put that "done" on a new line - no paragraph separator,
just a new line. (In general, perhaps when there's a non-default response
from an implied command, we should add an "implied command terminator
separator," which by default would simply be a newline.)
Rename the English-specific part of the library to make it easier to add
new languages in the future. In particular:
- Create a new US English subdirectory of the
adv3 library directory; use ISO 639/3166 codes for the naming
convention, so the US English directory is called en_us.
- Move us_eng.t to en_us/en_us.t
- Move us_eng.h to en_us/en_us.h
- Move msg_neu.t to en_us/msg_neu.t
- Add a new library, en_us/en_us.tl. Move the
English-specific source file inclusions out of adv3.tl and into
en_us.tl.
- Include the new en_us.tl library in the
auto-generated makefile for the new-project wizard in Workbench.
(We'll have to add a user preference at some point that lets the user
select the language library to use, rather than using en_us.tl
unconditionally.)
Add a general IF instructions module (like instruct.t from tads 2)
Fix nil dereference in transcript processing for ambiguous noun
phrase errors.
Fix command state management for suffixes so that suffixes are always
generated before the prompt. (We simply need to display <.commandbegin>
in intput.t before displaying the prompt.)
Add a new command state, stateNoCommand, for interactive input. In
this state, don't add any prefixes/separators/suffixes when reading
input. This should be used for yes/no answers, answers to
end-of-game prompts, and so forth - for anything where we need to
read input interactively in the course of a command.
Move en_us.t Thing vocabulary initialization code to VocabObject.
For commands like "put coin with jar," we're asking "what do you want
to put," which is a weird question. (This happens because we
interpret this as "put coin with jar in missing-np" and then resolve
the iobj first; this asks the usual question, which comes out the way
it does because the dobj tentatively resolves to an empty list since
it is a non-matching phrase. To solve this, we could use a dummy
entry in the tentative resolution list, to signal that we actually
have a dobj phrase, even if it's not resolvable to anything.)
It might be nice to catch constructs like this:
>open door with key
You see no door with key here.
In particular, it might be nice to recognize "<nounPhrase> <prep>
<nounPhrase>" constructs specifically, and flag them as "command not
understood" errors instead of "object not seen" errors. This could be
handled by adding a "badness" production for np-prep-np structures for
the prepositions commonly used in command phrases; by making this a
badness match, we'll match it as a last resort when we can't find a
valid verb phrase structure.
Add an extra category of reports, "extraReport", that's used to add
extra information that doesn't replace any default report. Use this
for the key-found-on-keyring report, since this report is supplemental
to any default Locked/Unlocked report and shouldn't suppress it.
Move the call to initializeVocab out of Thing.initializeThing, and
instead call initializeVocab directly from adv3LibPreinit.execute()
for each VocabObject. (This will initialize vocabulary for non-Thing
VocabObjects, such as Topic objects.)
Add a new inputManager method, promptForMore(), that shows the MORE
prompt, taking care of flushing the transcript and dealing with the
real-time clock. The method should take an argument specifying
whether or not to freeze the real-time clock; but even if the clock
keeps running, real-time events shouldn't be allowed to occur until
after the user acknowledges the MORE prompt, because otherwise we'd
defeat the purpose of the MORE pause, which is to wait for user
acknowledgment before showing any new output.
Change the Decoration class so that its generic not-important message
can be overridden simply by changing a property, rather than having
to override all of the Default handlers individually.
Likewise for Distant and Intangible.
Add a new 'isTopLevel' property, which determines if an object is a
top-level container within the game world. Set this to nil by
default for everything except Room objects, where we set it to true.
Use this property in evaluating isIn(nil): for an object with a nil
location, isIn(nil) returns true if and only if the object is NOT
top-level. This allows isIn(nil) to be used to sense whether or
not the object is part of the game world, recursively considering
the status of the object's containers.
Remove selfInReach from OutOfReach.
Fix problem with TravelConnector.verifyTravel(): when this routine is
called, gVerifyResults is not reliably set to a valid list. In
particular, gVerifyResults must be set to a valid result list from
TravelAction.verifyAction().
Turn on the sense cache while generating the status line. Displaying
the status line can involve some substantial sense calculations, so
enabling caching while generating it can improve overall response
time noticeably.
Fix 'follow wall' (generates nil pointer error)
Fix 'throw x at wall' (or floor or other room parts - nil pointer error)
Fix nil pointer error when addressing an ambiguous target actor.
>sit on darker chair
>sit on chair
Which one?
>darker
This is allowed, but shouldn't be - the reply should be "you're
already sitting on the darker chair." (The problem is that we verify
the lighter chair as okay, and we remember that we verified something
as okay but don't bother remembering what; we need to track which
objects we verify as okay individually.)
Make searching a surface (or looking in a surface) show the surface's
contents.
Check bulk when adding a new item to a surface. (This will fix the
weird inconsistency with chairs that allows you to put an object
on a chair you're sitting on, even if there's not room for you to
sit on the chair when the object is already on it.)
Reduce the verification logical rank of non-Food objects for >TASTE.
>sit on chair
>look
... You contain a keyring ...
(We shouldn't mention your contents in that manner. The problem is
that Actor inherits contentsListed from Fixed; Actor should override
contentsListed and set it to nil, since an actor's contents shouldn't
be listed in the ordinary fashion in a room description.)
Show object contents for EXAMINE and LOOK IN to full depth.
Searching (or looking in) a room part shows contents as being "in"
it, which isn't appropriate for walls, ceilings, or floors.
For THROW AT FLOOR, perhaps we should have a message other than
"Dropped" - something indicating the action is more violent than
just setting the object down.
Make contents listings go to arbitrary depth. Rather than showing a
separate list for the contents of top-level list items, show the
contents of each item parenthetically, and then recursively add these
parenthetical lists to show all contents. (Only do this when the
ListRecurse flag is set.)
Add a new Thing property, contentsListedSeparately, that allows an
object to control whether its contents are listed in the new in-line
style, or as the traditional separate list. Make this nil by default.
For "tall" listings, show contents of unlisted top-level items in
separate lists after the main list, just as we do for "wide" listings.
Remove the need to call showContentsList() separately after showList();
instead, call this automatically from showList() if the ListRecurse
flag is set.
Throwing destinations aren't working quite right. Refactor things a
bit: rename getFallDestination() to getHitFallDestination(), and give
it the previous container in the path as a parameter, so that it can
decide whether the object is being thrown from inside or outside (or,
in the case of a multi-location item, the source location). Make
things drop to the floor in most cases; games can override to make
things fall into intermediate containers when desired.
Change Actor.standUp to use reportFailure() when already standing.
>out
Out of what?
>e
You can't get out of that (the east wall).
It would be nice to treat such responses as new commands. We could
probably adopt the heuristic that we give the new command
interpretation priority over the noun phrase interpretation of a reply
to this prompt. Anything that looks like both a syntactically valid
noun phrase and a syntactically valid command probably matches the
noun phrase syntax only because it's extremely abbreviated; the
chances of the verb phrase match being an accident are somewhat less.
In addition, it should be less confusing to the user to treat the
response as a new command when a noun phrase was intended than vice
versa, and it should be fairly obvious how to force a noun phrase
interpretation (adding "the" would do the trick, for example).
Can we go to arbitrary depth for contentsListedSeparately? In particular:
>i
You are carrying a duffel bag (which contains a pen cup). The
pen cup contains four pens.
Right now, we're not adding that bit about the pens, because we think
we're done with the top-level list, as we've already listed
everything: we scan the duffel, and decide that we don't need to
recurse into it, since it's listed and has in-line listed contents.
What we'd need to do is add another recursive descent to look for
everything that we listed at the second (or deeper) level that has
contentsListedSeparately.
Tweak the parser's command match-tree ranking mechanism a little, so
that differences in the number of occurrences of a problem are counted
in a second pass, after we've exhausted the possibility of any
differences in the presence or absence of problems. This will make
slightly better choices in certain cases, such as when we have two
separate noun phrases in the action, and one interpretation treats
only one as ending in an adjective while the other has an adjective
ending in both phrases.
Add a profiling facility, to gather statistics for performance
optimizations in the library.
Rename senseInfoList to senseInfoTable, and make it return a
LookupTable rather than a list. Sense information lists are almost
always used as random-access tables, so we can speed up some
operations by representing these as lookup tables keyed on object.
Rename connectionList to connectionTable, and make it return a
LookupTable rather than a list. Since we want only one copy of each
object in the result, it's faster to build this as a lookup table,
where we can determine whether or not an object is already present
much more quickly than we can with a vector.
>e
>put cup in desk
>undo
(Takes back turns through "e". The problem is that we're marking a
remapped action as a nested action; it's not really nested, because
the original action is completely replaced and will never reach the
execution phase. Do not mark remapped actions as nested.)
Add Thing.specialDescOrder to control specialDesc the relative order
of specialDesc listings.
Change the format of the first-score-notification supplemental
message, which explains how to turn off score notifications: rather
than showing it as part of the same paragraph with the score, start a
new line and show it as a separate notification message.
Rename UnqualifiedPluralProd to DefinitePluralProd. In the English
parser, base implicitDetPluralOnlyNounPhrase(main) and
explicitDetPluralNounPhrase(definite) on DefinitePluralProd, since
"books" and "the books" should act the same way in English.
Simplify the Key and Keyring Attach and Detach handling by using
remapTo() in the keyring: remap "attach x to keyring" to "put x on
keyring" in Keyring.iobjFor(AttachTo), and remap "detach x from
keyring" to "take x from keyring" in Keyring.iobjFor(DetachFrom).
This removes a bunch of code from Key to handle the remapping at its
end - that code all predated the remap mechanism, and can now be
greatly simplified by using the new mechanism.
Fix a problem in vocabulary initialization: if a hyphen is used as a
placeholder in a vocabWords_ setting, the hyphen is added to the
object's part-of-speech property (it's not added to the dictionary,
but it does show up in the list for the part-of-speech property). It
should be omitted from the list, since it's not really a vocabulary
word.
Fix finishOptionUndo so that it can be used in daemons. This requires
a couple of changes. First, remove the transcript flush - this is a
relic of the pre-'transient' transcript mechanism and is no longer
needed. Second, daemons and fuses need to be able to use
TerminateCommandException and the like to exit, so we should catch
command-ending exceptions in the event manager.
Fix examining sightSize=large objects at a distance. (The problem is
that Thing.canDetailsBeSensed() is attenuating the ambient light level
for the sense path, which is unnecessary as the SenseInfo already does
this. canDetailsBeSensed doesn't need to go to so much trouble - any
SenseInfo with trans != opaque indicates the object can be sensed,
since trans will always be set to opaque for objects that cannot be
sensed.)
Add tok.t to system.tl. (This file isn't actually necessary to
compile a simple program, which is why it hasn't been in system.tl all
along; but it is necessary for any game based on the library, so on
balance it's probably better to include it in the default library.
Separate some action-time checks into check() routines:
Container.dobjFor(LookIn); Dial.dobjFor(TurnTo) (this could perhaps
benefit from some 'verify' checking of the literal value as well).
Add a specialDescLister, and use it for listing special descriptions.
Add a new property of Thing, specialDescListWith, which returns a list
group to use when showing special descriptions; use this new list
group generator in specialDescLister. This will allow special
descriptions to be grouped when desired, simply by using the standard
list grouping mechanism.
Add an "isCircularPassage" property to TravelConnector: when this
property of the connector is true, fully describe travel that winds
up in the origin. (We normally don't bother describing such circular
trips, but sometimes it's desirable to be able to do so.)
In en_us.t, add askDobjResponseProd=singleNoun for each action that
takes a singleDobj as its direct object.
In en_us.t, remove the redundant syntax for "walk in/into" and "go
in/into" from VerbRule(GoThrough), as these are already handled by
VerbRule(Enter).
In en_us.t, use tags wherever literal quotes are used, rather than
ASCII-34 quotes.
Convert the parser to use the new Dictionary API. In the English
parser, install a StringComparator to perform case folding and
truncation matching. Remove case conversions from the English
tokenizer, as they're not needed with the StringComparator.
Refactor the travel connector operations so that everything turns into
a TravelVia on its connector. Handle direction commands ("go north")
by using replaceAction(TravelVia, connector). This simplifies the
travel verbs, and makes the task of defining travel connectors more
consistent with defining other action handlers.
Move the travel connector checks for dark-to-dark travel and for
travel barriers into the TravelVia check() handler.
Rearrange the room description code (lookAroundPov, etc) so that
lookAround can be called on any object. To do this, move all of the
room description mechanism from BasicLocation into Thing, and work the
NestedRoom code into the Thing code for descriptions.
Add a return value to basicEventManager.removeMatchingEvents,
indicating whether or not a matching event was actually found.
Fix getTenativeLiteralText argument mismatches in parser.t when
calling getLiteralText.
In MessageBuilder.generateMessage(), for the case where we're
substituting '{actor}' but there's no current action, use the current
gActor value if it's not nil, or gPlayerChar if gActor is nil (rather
than always using gPlayerChar - using gActor instead allows a daemon
or other non-action message producer to set gActor to indicate the
actor doing whatever it is that's generating the message).
In Actor.addPendingAction() and addFirstPendingAction(), take
a resolved object list as the last varargs-list argument, and pass
it to the PendingCommandInfo constructor.
Hide Fixed objects from "drop all".
Add some more default verbs: CutWith, Kiss.
Add RoomPart.isIn(). Consider a RoomPart to be within a given location
if its room part location is equal to or within the given location, or
the inherited isIn returns true.
Add some more string-quoting styles to the English tokenizer: `like
this', and with typographical ("curly") single and double quotes
(\u2018 and \u2019 for single quotes, \u201c and \u201d for double).
Add a quotedStringPhrase production type, for rules that explicitly
call for quoted strings (but not unquoted literal text). Use this
production to build the quoted string rule for literalPhrase.
Add SAVE, RESTORE, and SCRIPT command variations that take quoted
strings giving the filenames to use.
Traveler.travelTo in a daemon dereferences gAction - check to see if
gAction is nil, and skip that step if so.
Change all uses of 'seen' to use the seenBy() and setSeenBy() methods.
Move these from BasicLocation into Thing so that all objects uses them
uniformly.
Bug: TERSE mode doesn't show full descriptions on first entry.
Some of the objects shown in the introductory room description aren't
getting marked as seen. (The problem is that Thing.lookAroundWithin
is using gActor in a few places where it should be using the 'actor'
parameter.)
Bug: NoTravelMessage is commented as being an apparent connector, but
is implemented as a non-apparent connector. Leave the implementation
the way it is, but fix the comments, and add a new class that acts the
same as NoTravelMessage but has an apparent connector (call it
FakeConnector, perhaps).
Rename LiteralAction to LiteralTAction. Add a new LiteralAction
class that takes only a literal phrase as its object (i.e., no other
direct object), for commands like "say random stuff".
Note that DefineLiteralAction is likewise renamed to
DefineLiteralTAction, and we add a new DefineLiteralAction for
defining an action with only a literal phrase for its object.
Add a PostUndoObject class, analogous to PostRestoreObject.
Reduce parse rankings for pronoun phrases vs noun phrases: if a word
is explicitly defined as noun, it probably should be a stronger match
than a pronoun interpretation of the same word.
Consider this command:
>put ball in asdf
Which ball do you mean, the red ball, or the green ball?
>red
The story doesn't know the word 'asdf'.
>oops box
Which ball do you mean, the red ball, or the green ball?
The issue is that OOPS retries the entire command with an updated
token list, and as a result, the disambiguation work from the first
attempt at resolving the objects is lost for the second iteration.
The reparsing is necessary, since the corrected typo could change
the entire meaning of the command.
One possible solution: add a resolution pass specifically to catch
unknown words. Don't do any other resolution on this pass - just
deal with unknown words. This would ensure that we process OOPS
before we ask any disambiguation questions.
Another possibility: keep a set of answers to questions, and the
conditions under which the questions were asked. Clear the list
each time we start a new parsing from scratch, but let the list
survive if we reparse a token list. Whenever we're about to ask
a question, look to see if the same question has been asked with
the same conditions, and if so, reuse the same response.
All
Missing noun phrases: prompt for input when PC gave command
Missing noun phrases: use a default when possible
Object announcements: include a preposition when appropriate (announcing
indirect object, announcing direct object with verb taking a preposition:
">dig \n (in the dirt)")
Pronouns
Again
Again: should we use disambiguation information from past commands?
For example:
>take book
Which book do you mean, the red one, the blue one, or the green one?
>blue
Taken.
>g
Should this 'take blue book' or should it ask...
Which book do you mean, the blue one, or the green one?
Right now we use the literal tokens from the original command, so we
ask the disambiguation question again. This is exactly what we want
in cases with indistinguishables:
You see five silver coins here.
>take coin
Taken.
>g
Taken.
But in cases where we have distinguishable objects, we probably want
the resolved objects, not the words.
We probably want to distinguish this way: if we had indefinites or
indistinguishables, resolve again from the original text. Otherwise,
use the original resolution.
disambiguation: we're not keeping the full list properly in cases of
indistinguishables:
>take coin
Which coin, a gold coin, or a silver coin?
>coin
Which coin, the silver coin, or the gold coin?
The second time around, the cardinality of the full list should not change,
so we should be asking the same question as the first time.
Preconditions
We need 'touchDobj' and 'touchIobj' preconditions - use these for all
objects that require that the object be physically manipulated by the
actor but not necessarily held. These are required to deal with things
like the contents of a closed transparent container, where an object
can be in scope but cannot be manipulated.
As part of this, we need to find a way to say what object blocks a
sense path opaquely, so that we can say why we can't touch something
that we can see. There are two main cases: containers are in the way,
and connectors are in the way.
Bag of holding: when an actor's hands are full and the actor is trying
to take something, pick a held object and try to find a bag of holding
for it. First, check the object's preferredHolder property - this
gives an object or class that the object prefers as its bag of holding.
If the actor is holding such a holder, move the object to the holder.
If we can't find any affinity, look for any object the actor is holding
of the generic BagOfHolding class.
Examples of specialized bags of holding: wallet, key ring, purse, duffel
bag.
Formatting: where does the blank line come from before an implicit
message for a single command?
use disambiguation filter even for indefinites, so we pick the best
possibility if there are objects of different likelihoods
"The red one" should work for exclusion lists. It should probably also
work in general - although 'one' should bind more tightly to an actual
name, in the absence of a name it should serve as a generic noun.
"drop coins" - should we consider only the ones we're carrying? In other
words, should we run verification and include only the logical ones?
Probably - if there are some logical and some not logical, apply the
command only to the logical ones; otherwise, apply the command to all
of them.
"take both coins" - really wacky
"take two gold" should work (i.e., quantified ending in adjective)
consider:
>take both coins
Which two (of five gold coins or three silver coins) do you mean?
>two gold
You don't see that many coins here.
The problem is that we disambiguate with the reduced match list.
>take coin
Which coin, gold, silver, or copper?
>all but copper
no worky - might even be a gram prod bug, since we seem to have a weird
firstToken/lastToken value in these cases
"take books except red" - disambiguation of the exclusion list should be
limited in scope to the resolved plural list.
"take books except silver" - "You see no silver here" - this should
ignore the missing object.
"take coins except copper" - the 'copper' should implicitly be plural
rather than indefinite so that we skip all of the copper coins if there
are several
>take gold coin, gold coin
gold coin: Taken.
gold coin: You're already carrying the gold coin.
When we have multiple equivalent items in scope, and we mention several
items in a list, we should pick separate equivalent instances for each
mention.
This should apply to disambiguation responses, too:
>take coin
Which coin do you mean, a gold coin or a silver coin?
>gold, gold
"take any ball except green" - takes the green if there's nothing else
left
add verification for pre-conditions - objHeld, for example, would boost
the likelihood for a held object
add the new pre-execution check as a separate phase (checkXxx?)
output capturers - one at a time
add object affinities for bag of holding
key ring - not just a bag of holding, but automatically adds items
as they're picked up when the key ring is in inventory
Key ring messages: we should check for a non-default report, and if there
is no non-default report we should include more details: "You take the
key and put it on the keyring."
Probably should have an objDirectlyHeld precondition. This one would
be used for commands that physically move an object; objHeld would
continue to be used for commands that merely need to do something
with an object. So, "put x in y" would require that x is directly
held, while "unlock x with y" would only require that y be nominally
held. For objDirectlyHeld, we'll make the implicit command silent
if the object is being indirectly carried, so that we don't get weird
things like this:
>i
You are carrying a paper bag. The paper bag contains a key.
>drop key
(First taking the key)
Dropped.
If there's no doXxx, fail verification with a generic Illogical('you
can\'t do that').
For travel: shouldn't we have a verify phase for moving the actor?
need to export propNotDefined in a system file
take key: if it started on the keyring, we should take it off the
keyring
Travel connectors
Fix 'i tall' listings to show contents with a better format
Move senses to a separate module.
>get coin
Which coin, a gold coin, a copper coin, or a silver coin?
>coin
Which coin, a copper coin, a silver coin, or a gold coin?
The re-ordering on the second round is weird - we should keep the order
stable if we can.
>get ball
Which ball, red or green?
>blue
You don't see that here.
The error shouldn't be "you don't see that here" - it should be more
like "that's not one of the choices you were offered." However, it
would be better to widen the scope again to the real scope, rather
than failing.
Add maxWeight and weight enforcement
Move pre-conditions to the objects. Keep the verb preconditions as
well, but verb-specific pre-conditions should never mention the
objects.
Mark objects for which we've displayed descriptions, for topic scoping
Add "module ID" objects, for library extensions. The "credits" command
should run through the module ID's and show a credit for each one.
Make styles into objects
Fuses and daemons
Need to apply logical tests again after running preconditions. (Try
this: "put rusty key on keyring" - it'll pick up the key for the
'held' precondition, which will put the key on the keyring, but then
the explicit put-on-keyring command will succeed, which it shouldn't.)
Generalize the showList mechanism to allow for other types of
hierarchies besides contents to be shown. Use the lister interface
to virtualize the hierarchy traversal.
Use the generic showList mechanism for the full score lister, rather
than using a specialized lister that does essentially the same thing.
Score change notifications: add daemon that senses score changes and
notifies between turns.
Do not count the time taken for implicit commands.
Add a "message generation context" that determines what is generating
messages. This object must be sensible to the player character for
messages to be generated. For convenience, define a callWithMessageContext
function that establishes the context, invokes a callback, then restores
the enclosing context.
When establishing a new context, we check to see if the generator is in
scope in the given sense. If not, we hide messages; if so, we show
messages.
A nested context should actually be considered a context switch,
because we don't want an enclosing hiding context to hide an enclosed
showing context. For example, if we are generating messages with
sight scope for Lloyd, and we have a nested context where Lloyd is
saying something and so we now are in hearing scope for Lloyd, we
want the nested context to show its messages if in fact Lloyd is in
hearing scope even if Lloyd is not in sight scope (maybe the player
character and Lloyd are in contact over a walkie-talkie).
NPC messages: only show if NPC is in sight of PC (using message generation
context)
Use message generation context for fuses and daemons. Allow events to
be created with a context object and sense object; whenever an event
is executed, establish the context for the duration of the event's
execution.
try this: bob, n. z. s. / n. z
We should see bob leaving on his third queued move, but we don't for
some reason.
Do we really want to remove the objects directly involved in a command
from the beforeAction and afterAction processing? It seems like it's
an unnecessary inconsistency.
Should consider directing report messages to the pc/npc message
objects. This would more readily allow separate messages for
reporting the results of pc and npc commands (for example, "Taken" vs
"Bob takes the book"). This is probably trivial, since we already
have the pc/npc message generator division anyway; we probably just
need to use it rather than verbMessages for generating report
messages. (If there's really a good reason to have a separate
verbMessage object, maybe we should have a per-actor verbMessages
object, the way we do with the default library messages generator.)
Missing blank line between commands on parser failures:
>bob, n. get silver coin.
>n
In room descriptions, list actors separately.
Don't let the room lister include actor inventory listings with the
main room contents listing.
Footnote system
Add an initExamineDesc, to parallel initDesc when an object is
in its initial position and is explicitly examined.
Check changes in object bulks for effects on containers. If a
container doesn't allow a change in child bulk, fail the command
that attempted the change. (For example, in a stretchy bag,
putting a new object into the bag should be disallowed if doing
so would make expand the bag so much that the bag would be too
large for its own container.)
Stretchy bags, that conform to their contents (in the sense that
the bulk changes according to its contents)
Use normal command result reports (especially ReportFailure) in
precondition checks.
For target of "put in", elevate likelihood for an item being held.
implement save/restore
implement restart
implement undo
For undoing commands to NPC's, show the NPC as part of the command
being undone: "undoing one command: bob, go north".
Add '\{' and '\}' (push/pop formatter state) output sequences.
Implicit commands should not set antecedents.
"Take All" should include the non-fixed contents of fixed containers
and surfaces within the room.
Status line
Footnotes: add FOOTNOTE MEDIUM mode, which shows new footnote
references but hides references to footnotes that have already
been read. This helps cut down on unnecessary clutter, since
a footnote reference is unlikely to be of much interest once
it's been read, assuming that footnotes are always extra
information that isn't required to finish the game.
Weird:
>get large red ball, small red ball and drop them
[works]
>get asdf red ball, small red ball and drop them
Don't know the word 'asdf'
>oops large
...you see no drop them here
Why does it pick out the right interpretation normally but can't
on a token reparse?
Add an "unobvious" verification mode that allows the command but will
not be accepted as a default.
Decorations
Add dobjCheck, iobjCheck, dobjGen, iobjGen equivalents
Add a default mechanism for listing the inventory of an actor as
part of the actor's description. ("Bob is carrying...wearing...").
Add a sorter method to the list group object, for simple control over
the sorting order
Add input font switch when reading a command
When defaulting a direct object of a two-object command, include the
verb's participle form in the default message:
>ask about watch
(asking Bob)
Most default messages are meant to be tagged onto the end of the
command line, but when we're adding a direct object to a two-object
verb, the added object goes in the middle of the command and hence
the default phrase doesn't sound right when tagged onto the end of
the command line. By adding the participle form of the verb, we
make it clear that the default message stands alone as a new sentence
(or sentence fragment, anyway) and is not meant as a missing
continuation of the original command text.
"show iron key" - since the missing phrase form of this command has
badness, this ends up being interpreted as "show key to iron", which
is silly. For command forms like this with two objects and no
preposition, we should probably add a non-badness single-object
grammar that asks for the missing prepositional phrase. How we
select the misisng-object phrasing over the two-object interpretation
is unclear, though.
Parameterize the amount of time it takes for an actor to give an
order to another actor.
try 'inflate raft and put it in bottle' - we don't get a blank line
before the 'put it in bottle' response, presumably because it has
an implicit command first
Floor/ceiling/walls for indoor rooms, ground/sky for outdoor rooms
Traverse the sense path for throwing an object at another object, and
make the projectile land in the appropriate place when an intervening
object prevents the throw from finishing.
Keep track of multiple logical results, and make comparisons based on
the whole list. Distinguish different reasons (using 'key' values)
for different logical results with the same ranking.
The idea is that if we have two objects that are both ranked the same
way on one axis but are distinguishable on another axis, we want to
consider the axis on which they're distinguishable. For example, if
we type "put silver coin in jar," and one jar is open and the other
is closed, we want to pick the one that's open. At the same time,
they'll both be ranked as equivalently logical on the basis that
neither is held by the actor. We don't want the being-held status to
override the open status, but we likewise don't want the open status
to override the being-held status if both are open but only one is
being held. The solution is to ignore the equivalent being-held
status when both objects have this same value, and to ignore the
equivalent open status when both have the same value, but still
consider these attributes when they differ.
Provide suitable default implementations for about 1.0e+6 verbs
"re-route" syntax - see, for example, keyring.iobjFor(AttachTo).
Use asDobjFor and asIobjFor:
iobjFor(AttachTo) asIobjFor(PutOn)
For travel: actorStanding pre-condition that automatically extricates
the actor from chairs and the like. We must catch dangerous conditions
in the verifier.
For missing iobj's, allow preposition in response:
>dig in dirt
What do you want to dig in it with?
>with shovel
Type/enter string/number on object
Type/turn to unquoted string?
In a two-object action, when assuming one object or the other, and
then asking for the other, we should show the defaulted one before
prompting:
>ask
(Bob)
What do you want to ask him about?
For disambiguation, never pick a 'not obvious' over an illogical, because
a 'not obvious' essentially counts as an illogical for the purposes of
picking objects.
It might be good to eliminate the redundant default announcement in
cases like this:
>ask
(asking Bob)
What do you want to ask him about?
>watch
(asking Bob)
It might be good to eliminate the participle when asking for a direct
object of a two-object verb and the indirect object is not yet known:
>ask
(asking Bob)
It would be better as simply
>ask
(Bob)
Turn x to string/number
Dials (turn, turn to)
Buttons (push)
Switches (turn on, turn off)
Levers (pull, push, move)
Doors
In the dark: should be able to list items you're holding by touch.
In the dark, 'look at' should indicate that it's the darkness that
prevents an in-scope item from being inspected.
In the dark, inventory should do something better than say "you're
empty-handed".
Climbables (such as stairs). These should probably just be travel
connectors that go up and down.
Check scope for preconditions - make sure we don't apply a command
to an object not in scope by way of a precondition implicit command.
For example, if you type "go north" in the dark, and there's a closed
door that you can't see, you shouldn't be able to open the door by
way of the travel precondition.
>go through passage
>again
This somehow keeps the old passage in scope. Must need to check
scope again for 'again'.
(The problem is that the last action is hanging on to its cached
resolver scope lists. We simply need to drop the old resolvers
so that we start the new command with new resolvers.)
climb up/climb down verbs
make sure we have makeOpen, makeClosed, makeLocked, makeUnlocked, etc. -
in other words, encapsulate these types of state changes the same way
moveInto encapsulates containment changes
Add an "inaccessible" verification failure - this would be more
disapproving than any "illogical" level and would be used for
"it's too dark" and "you can't reach that" types of failures.
The pre-condition execution sequence should probably be like this:
for (pass = 1 ; pass <= 2 ; ++pass)
run all verifiers
for each precondition
run this precondition, allowing implicit commands on pass 1 ONLY
if no implicit commands were executed
break
This would ensure that side effects of an implicit command are re-tested
through previous preconditions. Each precondition would only be allowed
to run its implicit command on pass 1 because this would prevent
infinite loops from conflicting preconditions - on pass 2, if the
condition isn't met by now, we'll assume it's not going to be, so
we'll simply fail the command.
"actor, give x to me" should be handled as though it were "ask actor
for x", via a replacement command.
actors: shouldn't allow taking possessions in general
Dark rooms: limit travel to adjacent lit locations?
Explicitly inherit base class vocabWords_ in initializeVocab() in
us_eng.t.
touch/feel verbs
Add verb "listen" (both transitive and intransitive)
Add verb "smell" (transitive and intransitive)
Enterables
keys and doors: add options for boosting likelihood for keys:
-
on the first successful use of a key, automatically boost the
likelihood for future disambiguation using the key if an option is
set (this might be the default option)
-
if the author wants the player character to know from the outset
which key goes with a door, the author can add the key to the
lockable's knownKeyList - this can be useful for games with obvious
key associations or key associations known from the outset to the
player character (for example, if the PC has a key to their own
house, the ought to know what the key opens)
locks and keys
lockable doors
Automatic key rings for unlocking doors - an "unlock" command with no
indirect object automatically searches for a keyring in the actor's
inventory, and automatically searches for a key on the keyring.
keyring: need to implicitly take keyring if not held when trying it
keyrings: message on success should be something like:
(trying each key on the keyring, you find that the iron key unlocks the door)
keyring search: when we already know the correct key, we should just
default it rather than search the keyring for it
implicit commands that use 'replace' should inherit original implicit
status (try 's' from the stair bottom - we get an "unlocked" report
for the replaced 'unlock door' command)
For Fixed, indirect the illogical messages through properties, to
allow easier customization.
Whenever any message appears before a room description in a command
sequence, show a blank line before the room description. This applies
especially to travel, but is generally desirable for any command that
shows a room description.
Put iron key in duffel; unlock iron door - won't default the iron
key, because the keyring takes precedence by virtue of being held.
The problem is that the objHeld precondition is applying a likelihood
of 80 because the iron key isn't held, and the held keys are getting
100's. Maybe we need to reduce the likelihood of any other keys or
keyrings when the known key is present, but this seems like a poor
special-case hack for a problem that is likely to arise in other
contexts.
To solve this, add a 'priority' to logical results. Use low priority
for precondition rankings, because these are inherently of lower
importance than unique attributes of the objects themselves.
Add an 'exit only passage' to be used as the receiving end of things
like trap doors, chutes, and so on.
Message travel connectors: simple connector that shows a message as the
connector is traversed.
preserve the case of tokens in literal phrases
Light/dark changes should be noted at the end of each turn.
Light/dark changes should be noted before/after each daemon is
dispatched.
>move me to
What do you want to move it to?
Could we substitute "yourself" for "it" somehow?
Trap doors - in particular, the arrival message should indicate that the
trap door automatically slams shut after the actor arrives.
Possessives - "take his box", "take bob's box"
possessives - problem with disambig:
>get key's jar
Which key's do you mean...
That should just be 'which key do you mean', not 'which KEY'S'
possessives - plurals
possessives - five of bob's keys
possessives - bob's keys except the green one
get all of bob's keys except the rusty and iron ones - even when this
is just one key, we should announce it as though it were a multiple
object (so we need to set some flag)
possessives:
>bob's
unknown word ''s'
possessives - problem with disambig:
>get key
Which key do you mean...
>get bob's key
The word ''s' is not necessary...
The problem would seem to be that we don't handle 's in disambig
responses and treat it as an unknown word.
>look under me
You see nothing unusual under you.
We should make that second 'you' into 'yourself' instead. In general,
can we change an objective case use of an object already used in the
nominative should to a reflexive instead?
possessives - disambig response should allow 'bob's' as a choice,
even if it's not offered
possessives - "get keys except bob's" - need special grammar for
just a possessive in this context
>bob, x key
Which key?
>quit
This command cannot be directed to another character...
Distant items
Finish topic-verb implementation
Consider:
>lock door
(first closing the iron door)
(with the iron key)
Locked.
The defaulted indirect object is misleading. This should instead be:
>lock door
(with the iron key)
(first closing the iron door)
Locked.
The problem comes from the fact that "Lock" has an objClosed
precondition for keyed lockable; "Lock" doesn't need this
precondition, because "LockWith" has it. Removing the precondition
will get the ordering right.
truncated adjectives seem worse than unknowns in ranking:
>type hello on typewri
do not filter the 'all' list with hideFromAll when looking for default
objects
>get iron key
You take the key and attach it to the keyring
>get keys
brass key: You take and attach...
rusty key: You take and attach...
iron key: Taken.
It's irritating that the last one is taken off the keyring. Should
we leave items out of plurals when we have multiple logical items of
different logicalness? (This isn't a completely academic problem,
since you could in practice have a couple of new keys in a room that
you want to pick up, without detaching keys you already have on the
keyring. One easy way to solve this would be to use a different
command for detaching from the keyring - require 'take key off
keyring', and say Illogical('if you want to take the key off the
keyring, say so'); but this could be irritating when you just want to
take the key off the keyring.
follow: should replace command with actual command used for last
observed travel; alternatively, reproduce the preconditions and
verification for the travel via the connector
Player character vocabulary - either add switchPlayer to fiddle with
the dictionary, or have some other way to route 'me' and 'my' to the
current player character (using matchName, for example).
Move travel preconditions into the connectors after all. Just create
an openDoor precondition type that is instantiated with a specific
door to open, rather than attaching it to a command object.
send bob into dark. follow bob into dark, then:
>follow bob
Bob is right here.
>bob, u
You see no bob here.
>bob, n
>g
We're not checking to see if the actor is still in scope.
>n. open door.
>close door and sit down
--> endless loop - weird problem with token indices for 'sit down' part
of grammar tree. The token indices for the second predicate are for
the entire command, strangely, so we think we need to parse the entire
phrase again and again.
This has something to do with the empty dobj noun phrase. Same thing
happens with 'close door and open' (which also has an empty dobj),
but not with 'close door and look' (which takes no dobj) or 'close
door and sit down on floor' (which has a non-empty dobj).
when changing sensory contexts, don't flush output; instead, consider
sensory context when queueing reports, so that we simply don't queue
a report unless it's visible in the sense context
when a door closes from the other side, show a message about it
Status line messages for nested rooms, sitting, lying
Standing needs to be able to specify what you're standing on in some
cases, such as platforms. Is it only the floor that won't?
when standing, make sure we move actor to room
when sitting/standing, make sure we do all the necessary work of
travelTo
Chairs, beds (make Room not Fixed? or make a deeper base class for
BaseRoom, with Room: Fixed, Room?)
sit/lie on floor: actor doesn't actually need to stand first, if the
actor is already sitting/lying in the room. In other words, if we're
just changing from sitting to lying or vice versa, and not changing
containers, we don't need an intermediate standing state. We need
something like standing-or-in-obj instead.
sit/lie/stand on - when checking that we can enter a nested room, make
sure we're in the immediate container of the nested room
sit/lie: need to configure prep for 'on' vs 'in' (since we 'sit on'
some things but 'sit in' other things, and likewise for 'lie on'
vs 'lie in')
sit on floor, then sit on chair -> no intermediate 'stand'. This might
not be important, since it won't involve actual movement, but it's a
little weird, and it would be nice to make it consistent with the real
nested rooms.
'sit on floor' doesn't work if run from a doubly-nested room (or deeper)
sit on armchair, then stand on dais: "already standing on the dais".
Need to deal with an implied command that effects the change required
of the main command.
Might want a precondition for travel more general than 'standing',
to allow for standing in vehicles and on platforms
list actors in a nested room when describing the room
maximum seating capacity for a chair
in box: try 'e': "you can't do that from the main platform".
in box: in the dark, should probably have the enclosing room in scope
in box: when closed and we have light, definitely should have the
enclosing room in scope
in box: should not be able to try to get out of box when we can't
sense beyond the box - it should serve as top-level location for
the purposes of the implied actions in this case
When in box, and box is closed, list contents of box as top-level
contents of room.
Listing room contents should show contents of fixed items to any
depth. The first-level contents of anything not listed should by
default be listed.
Vehicles
traveling - don't use actorStanding, but use travelerReady instead.
If the traveler is an actor this turns into actorStanding (or whatever
is appropriate for the actor); for a vehicle it's probably nothing, but
could be used to check for things like the doors being closed on a car
or seatbelts being fastened.
Vehicles: when PC is riding, we get no description of the new room
Vehicles: put tricycle on platform; get on tricycle; ride tricycle:
we don't seem to move the tricycle off the platform first.
Vehicles: departing should use definite article
Vehicles: departing doesn't list riders (presumably they're out of
scope by the time the departure message is generated; need to save
sense info or something)
sample game: implement 'ride tricycle'
sample game: when getting on tricycle, mention how to ride it
Put x on floor: if we're in a nested room with a drop location that
isn't the main room, we'll perform a "drop" in the nested room. Should
actually move to outermost room if possible. (Maybe we need to leave
the nested room as a precondition.)
Vehicles: need a convenient way to put up vehicle barriers, so that
you can walk through a door but not ride through, and vice versa
'follow me' mode for an actor
recognize (in input) reflexive pronouns referring to target actor
(bob, examine yourself)
Allow things to be placed out of reach from within nested rooms
(canReachFromRoom).
change from LangThing to 'modify Thing', etc.
change "bob, tell me about x" into "me, ask bob about x"
'push/move dobj to iobj' - for things like building staircases ('push
crate to window'). Synonyms might include 'push/move/put dobj under iobj'.
Fix follow problem: "bill cannot stand in closed box" is reported
even though we're out of range of the sense.
Allow some types of nested rooms (platforms, booths) to be their own
follow locations, so we can follow an actor into such a room
Might want to keep pronoun antecedents per actor. When an issuer
gives a command to a target, the target should copy all of the
antecedents from the issuer.
for 'in' and 'out', by default ask for a direct object if there's
no evident travel connection for the direction
remove all preconditions from noTravel, as we know travel will not occur
and hence don't need to meet any conditions
'push obj north' (and other directions)
for "follow", track actors in vehicles as well as the vehicles
themselves
'draggable' class, implementing a general framework for pushing
objects from one room to another
add a push-travel barrier that works like the vehicle barrier, but
for traveling while pushing objects
Change the barrier mechanism so that barriers are listed on the connector,
rather than being part of the connector. This is needed because the
connector is in many cases directly addressable - putting the barrier
in front of the connector as a proxy connector doesn't work when you
can reach the connector directly, making an end run around the barrier.
Change the listing mechanism so that showList is a method of the
ShowListInterface class, to simplify argument lists.
Handle nested grouping. Use this for equivalents in groups.
For group listings, don't add a group when there's only one group due
to equivalence. We currently say this, which is awkward: "the jar
contains two coins (two copper coins)."
Equivalent listings - eliminate as a separate special case and simply
list equivalents using a listing group. Add an Equivalent Group
which implements a simple listing group for equivalents; by default,
the group simply shows the count.
For our silver/gold/copper coins in the sample game, make sure the
grouping mechanism can list like this: "eight coins (three silver and
five gold)". Note that the names in the parens leave off "coins" and
just list the number and adjective.
Move all of the lister objects into the messages module, and eliminate
the secondary indirection through the libMessages object. Simply make
the lister itself part of the messages module, and put the messages
directly in the lister.
Get rid of the unnecessary list flags LIST_WORN and LIST_INVENTORY,
replacing them with parameterization at the listing method level
instead.
Use the generic list mechanism to list actors in a room. By default,
each actor simply lists separately, but it should be possible to
group actors using the normal listing group mechanism ("Bob and Bill
are at their desks", for example).
Can we make the disambig list use the normal listing mechanism?
Add a "specialDesc", similar to initDesc but for more general purposes.
specialDesc should turn into initDesc when initDesc is applicable.
add "the former" and "the latter" as disambiguation responses
For nested rooms, break out the "staging location" test from
checkActorReadyToEnterNestedRoom into a separate routine. Use a
list of possible staging areas given by a separate property for
easier overriding.
For nested rooms, use a separate "exit destination" property for the
destination of a GetOutOf action, rather than assuming that we always
move to the enclosing location.
Add a "high nested room" that can't be entered except from particular
staging areas due to height. These is a fairly trivial
specialization: first, don't solve the problem using implicit
commands unless the actor has already solved it before; second, the
default failure message is something like "you can't enter that from
here; it's too high up."
Rename the '*predicate*' production classes to '*command*'
Base all command (formerly predicate) classes on a common CommandProd
base class
Add a resolveAction method to the command classes. This method
retrieves the action from the command. In English, this simply
retrieves the 'predicate' match tree object, which is always based on
action; other languages, especially those that use case markers
rather than word order to encode phrase-role information, can
customize this behavior appropriately for their grammatical systems.
When scanning command parse matches, we can immediately eliminate
any match for which resolveAction fails to find a valid action.
Use separate listers for room/object contents lists and inventory lists.
Use separate show-list-item methods for inventory items.
For keyrings, show contents of keyring in room/object/inventory contents
as a sublist: a keyring (attached to which are an iron key and a bronze
key)...
Allow queuing pending actions for an actor, not just token lists.
Make it possible to insert a pending action at the beginning of the
queue (for continuation actions).
Use a separate listing flag (isListedInInventory) for inventory
list inclusion. For actors, make this true by default, even though
actors are not listed in room descriptions.
Dispenser and Dispensable, for matchbooks and the like
Collective objects for cases like "book of matches" and "matches" -
when we type "take matches," we should interpret this as the book of
matches (i.e., "matches" == singular) rather than as the individual
matches ("matches" == plural).
Matches - flammable objects that are self-igniting
Matchbooks
Include a checksum in saved state files, and check at start of load.
This will help avoid attempting to restore a file that was corrupted.
Add a tentative pre-resolution pass, for the later-resolved object of
a two-object action, that runs before the resolution of the
earlier-resolved object. Don't perform full resolution on this pass;
specifically, don't count any problems against the ranking results,
and don't ask for help interactively.
Make use of this information in 'verify' routines for the
earlier-resolved objects when applicable. For example, for "take
from ", we resolve the indirect object first, so we
don't know the final dobj resolution when resolving the iobj; so, use
the tentative information so that we can rule an iobj illogical if
there's no tentative dobj inside the proposed iobj.
Might want to clean up the other-object business in askMissingObject
and related routines. This scheme doesn't feel properly generalized.
For that matter, we might want to rethink the whole scheme so that
it's subclassed by and handled in the action classes, rather than
parameterized, since the parameterization scheme seems hokey and
brittle.
Remove the otherObjectList and otherObjectWhich stuff when we've
cleaned up the corresponding code. This information should now be
available via the tentative resolution lists instead.
When lighting a match as part of an implied command, reduce the burn
time by one turn - this ensures that lighting the match as part of
another action won't artificially extend the match's burn time.
candles, torches, oil lanterns - flammable objects that must be lit with
another item, such as a match
defaults: when there are two or more equivalent items that could be
used as defaults, pick one arbitrarily
for implied transfers into a bag of holding, put the indirect object
of "take from" last in the affinity list
for implied transfers into a bag of holding, other things being equal,
transfer the least recently acquired object first
food items
Change the logic of checkActorInStagingLocation so that it doesn't
assume that we can reach a staging location just because we're
indirectly in the location. Instead, add a "choose staging location"
method so that rooms can override the staging location chooser with
appropriate special-case code when needed.
Add issuing actor and target actor parameters to resolveAction(), so
that the resolver can tell what reflexive pronoun phrases mean. (In
many languages, reflexive constructions are structurally part of some
predicates, and affect the meaning of the predicate, so the identity
of the speaker and of the subject must sometimes be known to
correctly determine the interpretation of the predicate structure.)
sample game: reject just plain "match" in the matchbook's matchName
touch scope: include contents of matchbooks and keyrings, so that
these can be used even in the dark
Add an associated odor and sound property to each Thing. This points
to an object that encapsulates the object's sensory emanations. Always
add these objects to scope when the associated objects are in scope,
with the same properties.
Make "listen to x" and "smell x" defer to the associated objects.
For scoping, a sound or smell doesn't have to place the main object
in scope, but rather can just put the intangible sensory emanation in
scope. For example, if an alarm clock is buzzing, the 'buzzing
sound' object would be in scope but not the alarm clock itself. (On
the other hand, some sounds, like a ringing phone, might be
sufficiently distinctive as to place the main object in scope. But
for those cases we can simply associate the sound/smell with the main
object.)
We need different odor/sound descriptions for different situations:
>listen to phone
It's ringing.
==> phone.soundDesc
>listen to ring [phone visible]
It's coming from the phone.
==> sound.soundWithSource
>listen to ring [phone not visible]
It sounds like a phone.
==> sound.soundWithoutSource
>look [phone visible]
...
The phone is ringing.
==> sound.soundHereWithSource
>look [phone not visible]
...
You can hear what sounds like a phone ringing.
==> sound.soundHereWithoutSource
Don't open a footnote when the PC isn't seeing the text with the
footnote reference.
Add sense emanations to inventory displays.
For sound/smell objects, add options to control ongoing announcements
of sensory emanations:
- always show on room description, or only show on
explicit "look" descriptions (and corresponding sense commands -
"smell", "listen")
- show every n clock ticks
- show every n clock ticks for m iterations, then go
to a secondary message every p clock ticks (for something like "the
phone is still ringing").
Room descriptions should be differentiated according to whether we're
explicitly looking or merely entering a new room.
When a noise's or odor's source is not visible, and the object is
examined (via "examine", or via "listen to" or "smell" or whatever),
it might be desirable in many cases to show the
apparent source,
which is to say the visually opaque obstructor, and describe the
sound/odor as coming from inside/outside/behind/whatever the
obstructor.
The obstructor can be easily found with
gActor.findOpaqueObstructor(sight, obj) (where 'obj' is the noise or
odor object). Once the obstructor is found, we'll have to generate
an appropriate message, which will require a new method parallel to
obs.cannotReachObject(obj) - perhaps we could call it
obs.describeSoundObstructor(obj) etc - which would display "The
ringing seems to be coming from inside the box" and the like.
This entire mechanism would not be used when the obstructor itself
cannot be seen, such as in the dark.
Consider:
>look
...
You can hear what sounds like a phone ringing.
>listen to ring
It sounds like a phone.
>x phone
You don't see any phone.
It would be better if that last line were:
>x phone
You can hear a phone ringing, but you can't see it.
To deal with this, adjust the touchObj and objVisible preconditions
so that it provides better feedback for an object that can be heard
but not seen.
Write a common main() that most games will use, with appropriate
hooks for showing the introductory text and so on. Or, perhaps
better, write a standard initialization routine that the game's
main() can call.
Provide an author-configurable option to process multiple orders to
NPC's synchronously - so, if you say "bob, go north, get all, go
south", the PC doesn't get a command line prompt again until Bob
finishes the whole set of commands.
It might be worth considering making the disambiguation responses for
&noMatchDisambig and &disambigOrdinalOutOfRange more interactive.
Currently, if you give an invalid response to a disambig question,
you get an error and no chance to retry. Something like this might
be more intuitive:
>take ball
Which ball, the red ball, or the green ball?
>third
There weren't that many choices - did you mean the red ball, or the
green ball?
>orange
That wasn't one of the choices - did you mean the red ball, or the
green ball?
However, if the response looks like a valid response but doesn't give
us a match, and it also looks syntactically like a valid new command,
treat it as a new command.
Generic script objects: an object for which we call a method automatically
each turn it's active. We'd keep a counter that we'd automatically advance
on each call. We should probably use this generic object to implement
text lists.
Generic text lists: an object that encapsulates a list of messages
to display, one per turn.
Text lists should be linkable to a common counter object, so that the
lists are synchronized on the same counter. This lets you have separate
lists for each room, but keep the timeline in each list the same.
It should be possible to associate a text list object with a room instead
of coding the room's text list in-line in the room.
Room text lists: a list of atmosphere messages we cycle through while
the player is in the room. This should be totally automatic, so you
just program the list and the library automatically sets up a daemon
to run through the messages.
When describing an object, show the special descriptions for any contents
of the object, just as we would list them in room descriptions. This
lets us see the special descriptions for contents, without having to
write any additional descriptive code in the container.
consider:
>take coin
Which coin, silver or gold?
We should probably keep the copper one in the list. In particular,
we probably shouldn't remove an item from consideration for
disambiguation just because it's less likely - once we've determined
that the noun phrase is ambiguous, we should offer all logical
matches, even when they're less likely.
consider:
>take coinc
Which coin...?
>take tricyc
The word 'tricyc' is not necessary in this story.
>take tricyc
Taken.
For some reason, truncated words are treated as misspellings when they
appear in disambiguation responses.
Can we cache sense information to speed up resolution a bit? In
particular, we should be able to cache sense information during the
noun phrase resolution and verification steps, since these do not
normally involve any game state changes; once execution begins,
though, caching should be turned off. (This is probably the optimal
set of trade-offs: on the one hand, the game and library should
never have to worry about cache invalidation - the library only needs
to turn caching on before starting the parsing phase and then turn
caching off before beginning the execution phase of a command; on
the other hand, we should derive substantial efficiency from caching
during the parsing phase, because this phase in particular tends to
evaluate a lot of sense information.
(Experimental code has been added, conditionally compiled based on
the SENSE_CACHE macro. We'll leave it on for the time being to get
some experience with it to see how well it works.)
Preparsing
Just-out-of-reach containers. Create a container type that puts its
contents in the distance for 'touch' purposes. This can be used for
things like items on the ceiling. We'll need a way to make this vary
depending on the source object, so that, for example, something could
be out of reach for an actor standing in the room but in reach if the
actor stands on a desk.
When using 'again' with a command that had an interactive
disambiguation response, the response 'that wasn't one of the
choices' is not appropriate since the player doesn't get to respond.
cache touch paths (and canTouch information) the same way we cache
sense paths
For OutOfReach containers, we shouldn't be able to touch the
container itself, since not only the contents but the container
itself is meant to be out of reach. To do this, add a PathTo
traversal for the last item in a path (and while we're at it, add the
symmetrical PathFrom for the first item in the path), and in
OutOfReach's checkTouchViaPath, treat PathTo the same as PathIn.
'down' from a platform should be 'get off platform' by default
add 'debug' verb (to break into the debugger)
Real-time events: on save/restore, adjust system clock basis to keep
everything in sync
Real-time events: design a class for the events
Real-time events: integrate with the scheduler and command reader
Real-time events: when an interruption occurs, catch any output and
automatically cancel the interrupted input
Fix problem with quoted strings in literal phrases (as in 'type
"hello" on typewriter')
Fix problem with 'throw': it's illogical to throw something at an
object within the object being thrown
Change all of the exclusion list phrases to terminalNounPhrase rules -
otherwise, when they exclude everything, they get misinterpreted as
((all but x and y) and z) which turns into (z and z). Everything with
a 'but' should turn into a terminalNounPhrase, just like the plain
'all but <list>' rule.
Make 'me' symmetric with 'yourself' in the grammar. In particular,
'me' should refer to the issuing actor, which is not necessarily the
player character.
fix problem with "yourself, jump"
For chairs and the like, we might want to consider an object owned
by an actor if the actor is occupying the object (so, "bob's chair"
is the chair bob is sitting on).
When taking an item, reduce the likelihood that an item being carried
by another actor is the one being taken.
Provide a way of distinguishing lit and unlit equivalents in a list.
Likewise for worn and unworn objects. Should probably add another
list group object after the equivalent grouper, and in this object
we should provide one group for lit objects and another for unlit.
The desired output is something like this:
You are carrying three matches (one lit), two flashlights (one
providing light).
Move the inheritNext into the behavior as the default native 'inherit'
instruction. Remove inheritNext from library source files.
the dagger shouldn't show up in the 'x desk' description when the
dagger's initial message is still being shown
Add macros to make grammar rules for predicates easier to read.
Improve the module names (adv3g -> parser.t, adv3v -> verbs.t, etc)
provide .t3m, .tdc for sample game with proper directory set-up
accept 'all
' and the like
build the #include file list in Workbench automatically when creating
a project, and when explicitly asked via a new command "scan source
files for #include"
compiler: warn on finding a backslash outside quoted text in front of
anything but a newline
Add a simpler, more structured way to add a single paragraph break.
Use a new pseudo-tag, "<.p>", to indicate a paragraph break; process
this tag just before writing output to the console.
All library input functions should coordinate with command reports to
turn off output capturing.
Add a finishGame() function offering restart/restore/undo/quit options
Build a proper set of Workbench sample games
Freeze the real-time clock while we're waiting for inputFile results,
while we're saving/restoring a file, and while we're waiting for an
inputLine when real-time events are not allowed. All of these should
be considered to be outside the normal real-time flow of the game,
so none of them should consume any game real-time.
In showList: mix groups and singles in the original order of lst,
rather than displaying groups and then singles separately. This is
important because we're not preserving the original ordering when
the list is sorted and it has subgroups. We can't guarantee
that we'll preserve the sorting order, of course, since grouping
could put together items that weren't consecutive in the sorting
order, so let grouping prevail when there's a conflict; but when
possible, keep the order the same. To do this, we should have
a single loop that traverses the original lst; for each item, if
it's a single, show the single, otherwise show its group. Only
show the group for the first item in the group; once we match a
group, mark the group as used so we don't show it again.
'+' should consistently concatenate individual items from a
collection on the right-hand side, whether the right-hand side is a
list, vector, or array
'+' and '-' should work with an Array as the left-hand side
Delete class Array. We'll have to change the function object stuff
to use Vector instead of Array.
Make Vector+val and Vector-val return new objects - do not modify the
original vector in place
add Vector.appendAll() - appends list elements in-place; maybe
Vector.removeValue() and Vector.removeAllValues() to do '-' in-place
LookupTable.forEach: remove the 'key' argument to make its interface
the same as everything else's.
Everything with a forEach: add a forEachAssoc(key, val) method alongside
forEach.
for Openable, if the object isn't transparent looking in, implicitly
open the object for "look in"
Iterators: add getCurVal and getCurKey methods.
Fix bug: 'listen' or 'smell' shows no response when there are things
that nominally have Noise/Odor associations, but none of the Noise/Odor
objects actually has anything to say at the moment.
Dictionary: add a way to iterate over the entries in the dictionary.
For inputManager.inputLineBegin and inputLineEnd, use a new style tag
"<.inputline>" rather than coding "" directly.
This makes it easier for a game to customize the input font setting.
Add access to the "restore code" (the second argument passed to
restoreGame()) for PostRestoreObject instances by adding the value as
a class property of PostRestoreObject.
Change the Thing template that ends with "desc" @location to end
instead with @location "desc" (i.e., reverse the desc and location
property order). Since "desc" can be lengthy, putting the location
first will make the template more readable.
Add before/after command separation messages in libMessages, to allow
for finer-grained control over the formatting of command responses.
Check dark travel before checking any barriers - move the call to
gActor.checkDarkTravel into the TravelConnector methods that call
gActor.travelTo, since this must be called as a separate phase before
the travel connectors do any of their other work.
Make Illogical() the most illogical ranking, and make IllogicalNow()
slightly less illogical (i.e., invert the old ordering of Illogical
and IllogicalNow).
Add Action.setMessageParam(), to allow message processors to set
their own special message parameters that they can use in expansion
text. This would be better than constructing messages partially with
the "{it obj/him}" mechanism and brute-force string concatenation,
because it would allow the concatenated bits to participate in the
same processing steps, such as reflexive pronoun conversions.
Add a macro (gMessageParams) to simplify the syntax for adding new
message parameters.
"x me" doesn't work in dark (target actor of a command should always
be in scope)
Remove 'actorResolved' check from execCommandTokens - this check is
vestigial (it stopped being necessary when we started pulling out the
actor phrase after resolution and re-parsing the rest of the command),
and makes it impossible to have multiple levels of indirection, as in
"tell bob to tell bill to go north".
Refactor FirstCommandProdWithActor (parser.t) to move the actor resolution
stuff into a separate CommandProdWithActor that FCPWA inherits from.
>bob, get ball
Which ball?
>z
Bob waits...
The "z" should have a default target of the player character, not Bob.
Allow things like "tell bill to tell bob to go north". In particular,
fix execCommandTokens so that it doesn't assume that only one actor can
be resolved per command.
Keep first-on-line information in the PendingCommandInfo. (This requires
changing the interfaces to all of the Actor routines that create pending
command info objects: addPendingCommand, addFirstPendingCommand,
addPendingAction, addFirstPendingAction.)
Add a noun-list grammar rule for singleNoun, so that if we match a
noun list in a place where a single noun is grammatically required,
we can generate an appropriate message rather than failing to match
grammar.
Symptoms fixed: "ask bob and bill about box" -> "you see no bob and
bill here"; "give coins to bob and bill" -> "you see no coins to here".
Add a point-of-view argument to BasicLocation.lookAround, so that a
room can be described from a point of view other than the actor doing
the looking.
Fix: paragraph break in response to getting out of white box while
the box is closed (in the platform room). (The last report -
"Okay..." - is displayed on the same line as the beeper noise.)
Can we make every kind of noun phrase production derive from a common
NounPhraseProd base class?
Allow multiple actors per command line, tads2-style, as in "bob, go
north. look. bill, go south." (In tads 2, a new actor can be
specified after a period.) However, only allow this in "synchronous"
mode, where the issuing actor waits for all commands to NPC's to
complete before taking another turn; in asynchronous mode, do not
allow mid-command target actor changes, because it's too confusing.
Fix problem in touchObj precondition: we assume that we can find a
valid touch path, so we traverse the path without checking to see if
we got a nil path. In some cases (such as when the target object is
in a location not sharing any common container with the actor), there
is no touch path at all, so we need to deal properly with this
possibility.
Fix visual command separation for 'undo' after a multi-command line,
and for a multi-undo command line.
Modify/replace/delete grammar rules. Add new syntax:
modify grammar production(tag): :
;
replace grammar production(tag): :
;
To make this workable, the "production(tag)" names of grammar match
objects must be unique; change the compiler to enforce uniqueness of
these names.
Also, change VerbRule to include a tag: VerbRule(tag). This will
allow modify/replace to be used with VerbRule(tag) definitions to
replace library verb grammar rules.
Add a mechanism for remapping a two-noun-phrase action to a different
action after resolving first object. For example, allow mapping
ATTACK x WITH THROWING STAR to THROW THROWING STAR AT x.
To do this, add a new pair of properties to TIAction: remapDobjProp
and remapIobjProp; define these properties using the same template
style as the rest of the properties in DefineTIAction. Add a new
remapTIAction() macro that can be used from within a remap() method
to remap a command.
Note that only the first-resolved object can be remapped, because the
whole point is to effect the remapping before resolving the
second-resolved object, so that the second-resolved object can be
resolved using the rules of the new action rather than of the
original action.
In BasicLocation.lookAround, remove the point-of-view object from
the list of objects to be described, rather than removing the actor.
Move the grammar convenience macros (VerbRule, SingleDobj, etc) out
of adv3.h and into us_eng.h - these are all specific to the English
verb grammar, so they should be in the English-specific definitions
rather than the language-independent definitions.
If a Daemon is created with an interval of zero or less, it'll put
the scheduler into an infinite loop invoking the daemon, because the
daemon will be permanently schedulable. Force the interval to at
least 1 on creation.
Add a lookAround() method to Thing, to generate a description from
the point of view of the Thing. This is necessary to allow things
like remote cameras. This method should call the container's
lookAroundPov() method (which must also be added to Thing) with the
point of view set self; this algorithm will eventually reach the
innermost room-like object, which will display the appropriate
room-like description.
Add a PAUSE command, to stop the real-time clock for games that
use real-time events.
Add a grammar notation that specifies a match for one of several
vocabulary properties. With the '<prod>' grammar that was being
kicked around for a while, the notation '
' was
appealing, but without the angle brackets, it's not obvious what the
equivalent syntax would be.
Use angle-bracket notation, even though we don't use it for
individual tokens: <nounM nounN nounF>
Add an explicit 'grammar' declaration with no matchable rules, so that
production names can be declared without actually creating rules for
them.
make 'inherited' after 'delegated' more consistent with the regular
inheritance behavior (in particular, the relatively new dynamic
inheritance search algorithm will fail to find a class related to
'self' in the delegatee's superclass tree, so there will be nothing
to inherit; ideally, we'd have an 'inheritance target' in the stack
frame in addition to 'self')
NestedRoom.getTravelConnector refers to gActor. The reason the method
looks at gActor is that it wants to check if the actor can see the
containing room, so that it can get the connector from the containing
room. Add an 'actor' parameter to getTravelConnector(), so that we
can specify an actor explicitly and thus ask for travel connectors
outside of turn execution contexts.
SouthwestAction is missing from action.t
noTravelIn, noTravelOut, noTravelDown - these need to override
isConnectorApparent to return nil.
Add redirectTravelIn, etc, which do show isConnectorApparent = true.
Add newActionObj alongside _newAction: the new function takes a
pre-created action instance, rather than creating a new instance
itself.
Add a getBestMatch() method to ResolvedTopic to retrieve the
top-ranking object. By default, provide an implementation that simply
returns an arbitrary object from the strongest match list. Games and
library extensions could customize this to use a different topic
picker mechanism as desired, but providing a decent default
implementation would help authors get started without having to worry
about coding their own topic framework if they didn't want anything
specific here.
Add a PreRestartObject, analogous to PreSaveObject, to allow any special
actions to be performed before a restart.
execCommandTokens should probably switch the sense context upon
discovering a target actor different from the issuing actor, before
actually starting to execute the command.
Move all of the output management into discrete OutputStream objects.
Associate filters, notifiers, capturers with individual streams. By
default, create one stream for the main text and another for the
status line.
NOTE: This change affects all of the notifier, filter, monitor, and
capturer function interfaces. All of these formerly global functions
are now methods of the OutputStream class.
Regarding touch path calculation (see e.g. touchObj precondition):
should we be finding a touch path to an object in a separate location
not connected by containment, but connected by a SenseConnector?
Fix basicScoreChange reporting for changes of 1 (or -1) points: use
singular "point".
change the T3 Workbench .tdc format to use .t3m files
vmrun: respond to Ctrl+Break to break into debugger
"parse-debug" command: accept the command without the "on" or "off"
specifier, and simply invert the current mode.
Add a nounMultiList production alongside nounList. Use this
production in matching singleNoun, rather than using the regular
nounList, so that we don't create spurious additional matches for the
degenerate single noun phrase form of nounList.
For bags of holding, check (in Container.tryPuttingObjInBag) to make
sure that the object being moved into the bag actually fits, and
don't even try if it won't. This will avoid spurious attempts and
failures to move things in to a bag of holding implicitly after the
bag becomes full.
Refactor the CommandProd-based productions in parser.t to move the
methods into new classes, and base the defined grammars on the
classes. This will facilitate adding new commandPhrase grammar rules
by allowing the existing behavior to be re-used via inheritance.
Add a new Actor property (revertTargetActorAtEndOfSentence) that, when
set, tells the parser to consider target actor designations ("bob, go
north...") in effect only until the end of a sentence. When the
property is set to true, switch the target actor back to the original
issuing actor at the end of each sentence.
As a convenience, add a new StyleTag subclass, HtmlStyleTag, for tags
with different renderings in HTML and plain text modes.
Add some new style tags:
- <.a> - for <a> text (and use it for
all links we generate)
- <.statusroom> - for the room name portion of
the status line
- <.statusscore> - for the score/turn count
portion of the status line
Add inputManager methods getKey() and getEvent() that provide covers
for inputKey() and inputEvent(), respectively, the integrate with the
real-time manager and the reports gatherer in the same manner as
inputLine.
Don't use [badness] to reduce the priority of ordinal lists in
disambiguation responses; instead, use the ranking mechanism to give
ordinals a lower ranking than noun/adjective interpretations.
Fix looping problem with answering 'g' to disambiguation queries.
(Don't store the response to a disambiguation query until after we
actually know that the input is an answer to the query, as opposed to
a new command.)
Do not notify sense path in moveInto() when traveling. (Travel uses
the separate TravelConnector mechanism, which doesn't necessarily map
directly onto sense connections; we don't want to attempt to notify
sense connections of travel because we don't actually use them for
travel.)
When disambiguating interactively, if the response isn't in the full
list, check to see if it's in the full scope list. This allows for
cases where the player really wants to try a command on something
that we decide isn't logical - it's better to let the player try than
to deny that the object is among the possible choices.
We should test the scope list as a second pass rather than allowing
the full scope on the first pass. So, we should try first as we do
now, limiting scope to the narrowed ("logical") list; then, if that
fails, we should try again with the full scope list.
Add an equivalent of the tads 2 "->" syntax: this syntax allowed
routing messages for one object to another object. For example, if
we wanted to route "open desk" to "open drawer", we would put "doOpen
-> drawer" in the desk. Define a new macro:
dobjForwardTo(Open, drawer)
This is used in place of a dobjFor().
(This was from Phil Lewis, who suggested "reroute" as the name and
also suggested a slightly more general format: dobjReroute(drawer,
Open). It might also be useful to be able to specify the verb to
forward separately, so that you could handle a verb with both a
different verb and different target object, but the action is
probably the same in most cases, so it seems more convenient to be
able to omit it.)
In roomSmellLister (and roomListenLister), add a custom isListed
method that checks a new property of the target object,
isSmellListedInRoom (isSoundListedInRoom in the case of Noise
objects). Set these properties to true by default. Add an isListed
override to smellActionLister (and listenActionLister) that simply
returns true. This change allows an Odor/Noise to list in response
to a >SMELL or >LISTEN without also showing in the normal room
description, as it would by default.
Propagate the results of an action up to callers by returning the
CommandReportList. Actually, we'll have to return a list of
CommandReportList objects, since one CommandReportList is generated
per iteration for an iterated action. The aggregate list could be
stored in a CommandResults object, which could provide high-level
analysis methods (e.g., isFailure()) to interpret the action results.
To do this, return a CommandResults object describing the command
reports list from newAction(), nestedAction(), and so on.
Provide a way of specifying a subclass of CommandReportList to use
when executing a new or nested command. To do this, callers use
a new function that takes a particular CommandReportList subclass
to use while calling a callback:
result = withCommandReportsClass(
MyCommandReportList, {: nestedActorAction(bob, SitOn, chair) });
In the tokenizer, if a word with a "'s" suffix explicitly appears in
the dictionary with the "'s", do NOT make the "'s" into a separate
token - keep the whole string with the "'s" as a single token. This
allows for cases where the grammar has a literal token with a "'s"
suffix, and where vocabulary words (such as adjectives) are
explicitly defined with "'s" suffixes.
In TravelConnector, allow for a single barrier rather than a list
of barriers. If the travelBarrier property is not a Collection of
some kind, treat it as a single TravelBarrier.
Add a OneWayRoomConnector to make it easier to define a connector
that connects one room to another but not vice versa.f
Add checkTravelConditions() to TravelConnector. Call this from
checkTravelBarriers(). By default, this does nothing; instances
can override it to apply special conditions to the travel in lieu
of creating a separate TravelBarrier object when the conditions
don't need to be re-used in other connectors.
Fix indentation problem for grouped items in INVENTORY TALL lists.
Hide intangibles from 'all' by default. Noises and odors should
probably be included in 'all' for LISTEN/SMELL.
When trying to put objects into a bag of holding to make room to take
a new object, don't even try moving an object that contains the bag
of holding (because doing so will just fail with a message "the is already in that").
Need a better way to say "the one in the room" than showing the room
name, because that's not useful in input. Should say something
like "the match on the floor".
Once everything in a disambiguation list is a basic equivalent, we
could use the 'name' of the objects to display the prompt, rather
than using the input text.
In Thing.initializeEquivalent, deal with the possibility that we have
a base class with a separate equivalent grouper. Create a separate
grouper for the subclass by testing to see if equivalentGrouper is
defined directly by the class object. If there is an inherited
grouper, we must create our own separate grouper for the subclass,
AND we must take the superclass grouper out of the listWith for the
subclass (since it will inherit the base class listWith by default).
In action.t around line 1532, when we're adding the UnclearDisambig
flag, we might want to suppress the flag if the object we selected
and the objects we rejected are basic equivalents of one another.
This avoids weird situations such as
>light match
(the match)
when we have one match we're holding and another in the matchbook:
we choose the one we're holding over the one in the matchbook because
of the must-be-holding precondition to lighting a match, but mentioning
which one we're choosing is weird because it doesn't tell us anything.
>drop match
Which match, one of your matches, or the match in the matchbook?
>my match
Which match, one of your matches, or the match in the matchbook?
...etc. The problem is that we're taking "my match" to mean any
match in my possession for parsing purposes, whereas we differentiate
holding from indirectly owned. Ideally, we'd prefer to treat "my
match" as "the match I'm holding directly" when it's ambiguous.
Add a disambigName property, and use it instead of the ordinary name
when generating disambiguation prompts. (We'll need aDisambigName,
theDisambigName, and countDisambigName as well to round out the set.)
Add "x in y" grammar.
Add a mechanism for distinguishing equivalents with different states:
Which candle to you mean, the lit candle or the unlit candle?
-but not-
Which wax do you mean, the unlit candle or the seal?
-which should just be
Which wax do you mean, the candle or the seal?
Use this mechanism to create a base class for light sources. We
can use this for things like matches and candles. The disambig
name for an object is "lit x" or "unlit x", so we add the adjective
"lit" or "unlit" according to state. Check the adjective in parseName.
Use this same mechanism to specify by owner when owner is a
distinguishing factor - "which gas mask do you mean, yours, or
teeterwaller's?"
To do this, associate with each object a list of "distinguisher"
objects. A candle might include the "lit" distinguisher object, and
probably all objects would include the "owner" distinguisher. We'd
make a list of all of the distinguishables in common to the set of
equivalents, then run through the common set. For each one, we'd ask
the distinguisher if the list of equivalents we have is
distinguishable via this distinguisher. The "lit" distinguisher
would return true if the objects were all in different "lit" states,
and the "owner" distinguisher would return true if all had different
owners. We'd stop at the first distinguisher that can tell all of
the objects apart. The distinguisher would give us the method to
call in each of the objects to list its distinguishing name - the
"lit" distinguisher would call the litName property, for example,
which would display "the lit candle" or "the unlit candle"; the
"owner" distinguisher would display "yours" or "teeterwaller's".
The response would have to be handled by the normal disambiguation
response mechanism, so the objects would have to conspire with the
distinguisher to have the proper adjectives. This is easy, though,
because the objects are the ones displaying the adjectives in the
first place when they show the disambiguation list.
If we find no distinguisher that can tell all of the items apart,
we'd look for one that can tell at least some of the items apart, and
phrase it like this: "the lit candle, or one of the unlit candles".
If they select one of the ones indistinguishable with this
distinguisher, we'd iterate, and maybe pick it up with a separate
distinguisher next time.
If none of the distinguishers can tell any of the objects apart, we'd
simply choose one arbitrarily like we do now.
>take candle
Which candle do you mean, the lit candle, or one of the unlit candles?
>unlit
Which do you mean, your candle, or one of Bob's?
>bob's
(arbitrarily picking one of bob's several unlit candles)
Taken.
Note that for ownership distinctions, need to offer a way to refer to
unowned objects:
>get candle
Which candle do you mean, Bob's, or the other one?
-or- Which candle do you mean, Bob's, or another one?
[responses would include...]
>another / another one / another gold coin / one of the others
/ one of the other gold coins / other / the other / the other one
/ the other gold coin / other gold coin
Hide actors from 'all' for all verbs by default.
For "put in" and "put on", use roughly the same strategy as "take"
for generating the "all" list: include only objects that are directly
in the actor's location, or in fixed objects in the actor's location,
as "take" does, but also include objects that the actor is directly
holding. Keep the same iobj/in-iobj exclusions.
Break up TravelConnector.checkTravelConditions() into two separate
methods (canTravelerPass(traveler) and explainTravelBarrier(traveler)),
just like TravelBarrier.
Fix Thing.normalizePath() so that it makes two separate passes over
the list. It should apply the PathThrough tranformation first, then
the PathPeer transformation as a second pass. (Performing both
transformations both on a single pass can cause PathThrough
transformations to be missed, because the PathPeer changes the list in
such a way that the PathThrough transformation doesn't recognize a
needed change. The PathThrough condition could alternatively be
modified to recognize PathPeer operations, but it's easier to do
things in two passes. Note that doing PathThrough first is safe,
because it only adds to the list - it never drops any operations that
the PathPeer transformation looks at.)
Add distance differentiation for 'examine' descriptions (and for
'listen') and 'smell' descriptions as well). Specifically, add
distantDesc and obscuredDesc, and call them from basicExamine
according to the current point of view. Add corresponding methods for
'listen' and 'smell' descriptions.
Add automatic state listing to the equivalent grouper. Use a new
Thing method, getState, that returns a ThingState object. Use this
object to group equivalents by state and to show the state of each
item.
Use the ThingState object to provide the ordinary status description
of an object.
Use ThingState to provide automatic matchName() filtering by state.
Add a property to ThingState, stateTokens, that lists tokens that
can only be used in noun phrases referring to an object in the state.
Integrate Steve's exit lister package into the library.
It would be nice to have a sorting order for the directions, so we
list in a more canonical order: north, south, east, west, northeast,
northwest, southeast, southwest, up, down, in, out.
When two exits have the same destination, list them together: "south
(or out) to the front yard".
Don't show exits to dark rooms in the dark.
In listing exits from a dark room, maybe we should show exits leading
to rooms with light, since travel would be allowed to those locations.
Change the way we figure out if travel from a dark room to an
adjoining lit room is possible. Add a separate TravelConnector
method that specifically determines if the connector itself is
visible to a given actor in a given origin location when the origin
location is dark. By default, make a connector visible from a dark
location when the destination room is lit. This will provide the
customary behavior (i.e., dark-to-dark travel is prohibited, but
dark-to-light is allowed on the theory that some of the light from
the destination leaks through to the origin, making the connection
itself self-illuminating and thus visible even in the dark origin
room), while more clearly articulating the rule by making the
visibility of the connector the explicit factor in determining
whether or not the travel is allowed, and also providing a clean way
to override the default heuristic for visibility of the connector for
particular connections that should use a different rule.
Add an option to show available exits in the status line (using
a terse display that just lists the direction names).
Make Surface.PutOn behave like Container.PutIn with respect resolving
the indirect object using the tentative direct object list: if
everything in the tentative direct object match list is already on
the indirect object, flag the indirect object as illogical.
Sense Event Model: Create a new class, SensoryEvent, that can be used
to trigger transient sensory events that actively notify interested
observers of their occurrence.
Add convenience macros for remapTIAction that define the full remapping
for a dobj or iobj to another action, with the same or reversed noun
phrase order:
dobjRemapTI(OpenWith, UnlockWith)
dobjRemapTIReverse(FillWith, PutIn)
dobjRemapTIReverse(ThrowAt, AttackWith)
examineSpecialContents should probably differentiate according to POV
and sight viewing conditions.
Change CommandReportList to a SimpleOutputCapturer. We don't actually
want to process any notifiers or monitors when capturing report results,
because we turn them into a report list that can be reordered. We want
to wait until we actually display the reports to notify anything of the
output.
>i
You are carrying three matches (one lit), and a matchbook (which
contains two matches).
>x match
Which match do you mean, an unlit match, or the lit match?
>an unlit match
(the match)
The match is an ordinary match.
It would be better not to make an arbitrary choice here, unless they
explicitly said "any unlit match". We should have another go at
asking for detail in this case.
Exit lister: if we have multiple exits from a location with nil
destination names, we are incorrectly grouping them as though they
had the same destination name. The result is that we only see one
such exit in an EXITS list. Destinations with nil destination names
should be treated as unknown destinations.
Sort adv3.tl alphabetically (except for 'modify' order dependencies).
Add Dan Schmidt's hyphen-converting output filter to output.c
Add Dan's Thing.canBeSeenBy, Thing.canBeSeen, etc. (These are
convenience methods, especially for canBeSeen and its like, which
operate on the player character, saving a little typing by making
'gPlayerChar' implicit.)
Add Dan's Event.delayEvent, removeEvent methods
Add an event manager method that cancels an event based on the
obj/prop combination, to save work for cases where this uniquely
identifies the event (this way, the author doesn't have to go to the
extra trouble of saving the Event object reference if the event can
be uniquely identified without it).
Add a "mass" or "collective" noun property, for objects that refer to
collections of large numbers of smaller parts, or continuously-measured
quantities: "some popcorn", "some water". (Is there anything that this
changes besides changing aName to "some "? Should think about it.)
Add a lower-level base class for Switch, OnOffControl, that keeps
track of the on/off state and accepts "turn on" and "turn off".
Define the additional switch-specific verbs ("switch" and "flip")
only in the Switch subclass - this allows for adding on/off behavior
without making something look exactly like a light switch.
Add a GO BACK command to return to the most recent location (if the
connector in was reversible).
Keep track of the last "interlocutor" for each actor; this is actor
last addressed in a targeted command ("bob, go north"), or the last
actor in a conversational command - ASK ABOUT, TELL ABOUT, SHOW TO,
GIVE TO. For the conversational commands, if the actor is left out
(ASK ABOUT GOLD, TELL ABOUT STORM), use the performing actor's most
recent interlocutor as the default, if the interlocutor is still
within talking range.
Create a subclass of Actor, UntakeableActor, for an actor that can't
be taken or moved. Define an additional subclass, Person, to
represent human characters, and provide some custom "fixed" messages
for it.
Ensure that the SCORE and FOOTNOTE modules are completely modular,
so that they can simply be omitted from the build if not required.
Get rid of libMessages.openBracket and closeBracket; instead, add
a style tag, <.parser>...<./parser>.
Also, add <.notification>...<./notification> for notifications
(score changes, FOOTNOTE instructions, etc.).
Change withBrackets() to parserMessage(), for better documentary
effect.
Simplify the menagerie of output filters, notifiers, monitors,
and capturers, reducing everything to a single type (filter).
Enforce stacking of filters.
Get rid of the paragraph suppression immediately following input.
Instead, use the command sequencer to display the appropriate kind
of separation.
Rename CommandReportList to CommandTranscript, and rename
gCommandReports to gTranscript.
Add resolved object arguments to Actor.addPendingAction,
Actor.addFirstPendingAction.
Rearrange arguments to Actor methods addPendingCommand,
addPendingAction, addFirstPendingCommand, addFirstPendingAction to
move the firstInSentence flag first. This will keep everything
consistent with the rearrangement necessitated by adding the varargs
resolved object list to addPendingAction and addFirstPendingAction.
When a command is applied to several objects, and processing the
command for some of the objects involves implied subcommands, set
off the results for the objects with implied subcommands visually,
by putting an extra paragraph break before and after each result
for an object with implied subcommands:
PO Box: Taken.
red ball: Taken.
iron key:
(First putting the red ball in the duffel bag to make room)
Taken.
blue test booklet: Taken.
brass key:
(First putting the red book in the duffel bag to make room)
Taken.
gold coin: Taken.
gold coin: Taken.
Use a new style tag, <.commandsep>, for command separation,
eliminating the method call commandSequencer.startNewCommand().
Using a tag instead of a method call will work better with text that
doesn't reach the output stream immediately (for example, text that
is queued into a command transcript during command results display).
In Thing.construct(), initialize the new object's vocabulary words and
add them to the global dictionary.
Add information on the connector back, if known, to the EXITS display:
Obvious exits lead north, back to the east, and south to the den.
...north; east, back to the living room; and south, to the den.
For NPC's, the announcement/result format for implied commands should
probably be changed. In particular, we should probably just treat NPC
implied actions as full separate commands:
>bob, north
Bob opens the door.
Bob departs through the door.
For NPC's, don't allow interactive defaulting for an implied command.
For NPC's, if an implied command fails, we should fail with an indication
that the NPC needs to perform the implied command:
>bob s
Bob must open the door before he can do that.
>bob, open door
Bob must unlock the door before he can do that.
Get rid of gCommandReportClass and withCommandReportsClass. Instead,
add a parameter to _newAction() to specify the transcript class to
use.
Add a new BasicLocation method, getRoomPartLocation(part), which
returns the immediate container of the given room part, as perceived
in that location. Top-level rooms would generally just return 'self'
if they have the room part, nil if not. Nested rooms would generally
pass this up to their containers, because they don't have room parts
themselves in most cases. Nested rooms with their own room parts
would handle it themselves.
"x floor" - should list objects that are on the floor; likewise for
other room parts (walls, ceiling). Use a separate listing test
(isListedInRoomPart(part)) to allow objects to opt out of being
listed specifically in such cases.
For special descriptions, add a "room part location" property - this
is a purely advisory property used only for finding contents of the
floor and so on. When we "x floor", show special descriptions only
for objects whose roomPartLocation is the floor of interest.
Re-enable the transcript after prompting for interactive resolution
(reading an OOPS response, a disambiguation response, etc).
Add a method to Action to swap the roles of the tentative objects,
for use with the 'verify' forwarding for TIAction remapping. Add a
call to this method to the dobjRemapTIReverse/iobjRemapTIReverse
macros before they forward their 'verify' calls.
When an action is remapped, and we need to announce an object (such
as a defaulted object), announce the
entire action, not just the
object. For example:
>fill bucket
(dipping the bucket into the river)
>fill bucket
(pouring the coffee into the bucket)
This is important because the normal announcements are worded with the
intention of being tacked onto the end of what the player typed; when
the action is remapped, the action we're actually executing is different
from what the player typed, so phrase fragments that are intended to be
added to what the player typed are no long applicable.