Table of Contents |
Extensions >
Sysrules
Sysrules
Overview
“System Rules” module. This implements a number of rules and rulebooks to allow game authors to tailor various aspects of the library’s behaviour. The Rules extension is also needed to make use of Sysrules.
New RuleBooks and classes
This extension defines the following RuleBooks:
- Start-up rulebooks: preinitRules, initRules.
- Action rulebooks: beforeRules, reportRules, afterRules.
- Turn Sequence rulebooks: turnStartRules, turnEndRules.
Each of these RuleBooks has a similarly-named rule class associated with
it; for example the BeforeRule
class is used
to define rules belonging to the beforeRules
Rulebook. When using these specialized subclasses of Rule, there’s no
need also to specify which RuleBook they belong to, since this is taken
care of in the class definitions (e.g. if you define something as an
InitRule
it’s redundant to go on to define its
location property as initRules
).
Usage
Include the rules.t and sysrules.t files after the library files but before your game source files (the rules extension is required for this one to work). If you are not already familiar with the workings of the rules extension you should first read its documentation before attempting to use this one.
None of the rulebooks defined in this extension expects any parameters
to be passed to its follow()
method. This
means that when you define rules belonging to any of these rulebooks,
you do not need to worry about defining any arguments for their
follow()
methods. With the exception of the
reportRules, all the RuleBooks defined in this extension have a
contValue
of nil; this means that the default
behaviour is for these RuleBooks to execute all the matching Rules they
find, rather than stopping at the first one.
By default this extension does not alter the way the adv3lite library behaves (from a player’s perspective), but by effectively replacing one or two methods and functions with rulebooks it makes the library’s behaviour more customizable (by game authors) as well as providing a number of hooks for additional author-defined rules. This is best explained by discussing each of the rulebooks in turn.
Startup Rules
PreinitRules
The preinitRules RuleBook starts out empty, but may be used to define rules that run at the pre-init stage (basically as an alternative to definining PreInit objects). Such rules may be defined by explicitly locating them in the preinitRules Rulebook using either form of syntax:
preinitRule1: Rule
location = preinitRules
follow()
{
me.name = 'Joe Bloggs';
}
;
preinitRule2: Rule @preinitRules
follow()
{
...
}
;
Or alternatively, they may simply be defined as belonging to the PreinitRule class:
preinitRule1: PreinitRule
follow()
{
me.name = 'Joe Bloggs';
}
;
InitRules
The initRules rulebook likewise starts out empty, and can be used to
define rules that run at start of play (at the init stage). These can
again be defined as Rules which you explicitly locate in the initRules
rulebook, but it is probably more convenient to define them as rules of
the InitRule class (which are automatically located in the
initRules
rulebook). InitRules are used in
very much the same way as PreinitRules, and effectively provide an
alternative to defining InitObjects. They are thus quite closely
equivalent to “when play begins” rules in Inform 7.
Action-Related Rules
The function of the action-related rulebooks in this extension is not not replace the TADS 3 style action handling with a set of Inform 7 like rulebooks, but rather to provide some convenient rule-oriented hooks for certain aspects of the action-processing cycle, in particular the before and after notifications. Authors familiar with Inform 7 should therefore not expect the three rulebooks described below to work in the same way as the similarly-named rules in I7 (although the ReportRules are fairly analogous).
Rules intended for one of these three rulebooks should be defined using
the BeforeRule
,
ReportRule
or
AfterRule
class as appropriate. Each of these
has a currentAction property which identifies the particular action
that triggered the associated rulebook.
Rules that need to match particular actions can be defined using the Rule template, e.g.:
BeforeRule &Jump
// A BeforeRule that matches the Jump action
;
AfterRule [Throw, ThrowDir]
// An AfterRule that matches either the Throw or the ThrowDir action.
;
If the verify(), check() or action() stages of an action would benefit from rule-like behaviour, you can define a custom RuleBook to handle this and call it from within the appropriate verify(), check() or action() method, as in the Jump and DigWith examples given in the documentation for the Rules extension.
The description of the individual rulebooks below will list the rules they contain along with their priorities. The ordering of rules within rulebooks can then be changed (if desired) by overriding these priorities, and additional rules inserted where required. Remember that a rule with a higher priority is executed before one with a lower one. Note that as far as possible the priorities of the library-defined rules have been assigned so that additional rules with the default priority of 100 should normally come at a sensible place in the rulebook, although you are, of course, free to assign whatever priority you wish to your own rules to make them execute when you want them to.
Before Rules
The main purpose of the beforeRules rulebook is to handle before
action notifications. The beforeRules
rulebook
is defined in this extension with the following rules, which are
executed in the order shown:
- checkActionPreconditionsRule: (priority 10000). Checks the preconditions relating to the action (as opposed to those defined on its objects).
- actorActionRule: (priority 9000). Calls the
actorAction()
method ongActor
(the current actor). - sceneNotifyBeforeRule: (priority 8000). Calls the
beforeAction()
method on any current scenes. - roomNotifyBeforeRule: (priority 7000). Calls the
roomBeforeAction()
method on the actor’s current room (which in turn calls theregionBeforeAction()
method on any regions that room is in). - scopeListNotifyBeforeRule: (priority 6000). Calls the
beforeAction()
methods of all the objects in scope.
To define additional rules for this rulebook, use the BeforeRule
class. This inherits from ReplaceRedirector
as
well as Rule
and so provides the
doInstead()
method, should you need to use it.
Probably the best use for BeforeRules, however, is as an alternative
method of handling before notifications that don’t fit neatly into
existing beforeAction()
methods, either
because they apply to multiple objects, or because they apply
conditionally, and it’s neater to handle the conditions in a series of
rules than via a complex if statement. For example, you might write:
BeforeRule &Take
during = desperateHurryScene
dobj = [comb, penny, paperclip]
follow()
{
"You don't have time to gather up petty items
right now. ";
exit;
}
priority = 8500
;
Note three things here: first the use of the
exit
macro to stop the action, and second the
priority given to this rule so that it comes after the
actorActionRule
but before the notification
rules; there’s no point notifying scenes, rooms and other objects of an
action that isn’t going to take place. Third and finally, there’s no
need to use the @ part of the template here to specify that your
BeforeRule
is located in the beforeRules
RuleBook; in common with all the specialized Rule classes defined in
this extension, a BeforeRule
is automatically
located in the appropriate RuleBook.
Note finally that whether the beforeRules are executed before or after
the check phase will depend on the value of
gameMain.beforeRunsBeforeCheck.
Report Rules
The ReportRules perform the same function as the report phase of a TAction or TIAction, namely to summarize the action across all the direct objects it applied to in the course of executing a single command (e.g. “You take the bath bun, the red brick, and the jar of olive oil.”). By default, the reportRules rulebook contains two rules:
- reportImplicitActionsRule: (priority 10000). Displays any
pending implicit action reports (the
follow()
method of this rule ends withnostop
to ensure that the subsequent rules are considered). - standardReportRule: (priority 0). Displays the output of the appropriate report() method from the final direct object in the batch (i.e. carries out the normal library behaviour you’d get if this extension wasn’t present).
Note that unlike all the other RuleBooks defined in this extension, the
reportRules
are defined with
contVal = null
rather than
contVal = nil
, which means that the rulebook
will stop after the first matching rule it finds. This enables you to
write custom a custom ReportRule without needing to remember to use
stop
to ensure that the
reportRules
rulebook doesn’t go on to display
the standard report as well as your custom one. You’d typically write a
ReportRule to customize the way an action is reported under specific
circumstances, for example:
ReportRule &Take
during = desperateHurryScene
follow()
{
"{I} hastily snatch{es/ed} <<gActionListStr>>. ";
}
;
Note the use of the gActionListStr
macro here
to provide a list of all the direct objects to which the current Take
command applied; your report rules should always include this macro
where you want to mention the direct object of the command to ensure
that all the relevant direct objects are listed.
Finally, note that the reportRules will only run if no text was displayed at the action() stage of the action in question for the objects concerned.
After Rules
The afterRules RuleBook is used principally to perform the various
after action notifications. If you are
familiar with Inform 7, you should note that this quite different from
an I7 after rule (which takes place between the carry out rules and the
report rules). The afterRules
in this
extension are executed after the action is fully complete, and hence
after
the reportRules. They cannot
be used to change the outcome of the action in any way, but only how the
action is responded to. Moreover, if an action is considered a failure,
the afterRules
won’t be run (since you can’t
react to an action that didn’t take place).
The afterRules
Rulebook starts out containing
the following rules:
- checkIlluminationRule (priority 10000): checks whether the action has changed the actor’s location from lit to unlit or vice versa and, if so, either announces the onset of darkness (if it is now dark) or displays a room description (if it is now light).
- notifyScenesAfterRule (priority 9000): calls the
afterAction()
method on any current scenes. - roomNotifyAfterRule (priority 8000): calls the
roomAfterAction()
method on the actor’s current room, and theregionAfterAction()
methods of any Regions that contain that room. - scopeListNotifyAfterRule (priority 7000): calls the
beforeAction()
method on every object in scope.
There are basically two ways in which game code can make use of this rulebook. The first is to reorder the existing rules by reassigning their priorities, so that after action notifications take place in a different order. The second is to define additional AfterRule rules which implement additional responses to an action that has just taken place.
Turn Sequence Rules
The two turn sequence rulebooks contain rules governing what takes place
at the beginning and end of each turn. In addition, they provide
convenient places to define TurnStartRules
or
TurnEndRules
to be executed on every turn,
either near the start or near the end of the turn, depending on which of
the two rulebooks you use. In some respects this duplicates the function
of a Daemon, but at the same
time it gives you more control over precisely where in the turn cycle
and under what conditions your code is run.
TurnStart Rules
The turnStartRules rulebook starts out containing the following rules:
- updateStatusLineRule (priority 10000): updates the status line.
- scoreNotificationRule (priority 9000): displays any applicable score notification.
- promptDaemonRule (priority 8000): runs any PromptDaemons that are due to fire.
- commandSpacingRule (priority 20): displays a blank line, preparatory to displaying the next command prompt.
- startInputLineRule (priority 10): outputs an
\<.inputline\>
tag preparatory to displaying the next command prompt. - displayCommandPromptRule (priority 0): displays the command prompt (by default “>”).
This rulebook does not go on to encompass the actual inputting and
parsing of the command (a) because these operations are protected by a
try... catch
block that cannot readily be
replicated within the same rulebook and (b) because it is in any case
probably not a good idea for game code to modify that particular part of
the cycle.
To define additional rules to be run in the turnStartRules rulebook, use
the TurnStartRule class. For example, to customize the command
prompt during the desparateHurryScene
you
might write:
TurnStartRule
during = desperateHurryScene
follow()
{
"Hurry! ";
}
priority = 5
;
Note that with this definition the rulebook will continue to execute the
displayCommandPromptRule
after our custom
rule, so the complete command prompt will appear as Hurry! >. If we
wanted to replace the standard command prompt entirely we’d need to
add a stop
statement to the end of the
follow()
method of our custom rule.
TurnEnd Rules
The turnEndRules rulebook deals with end-of-turn processing such as running daemons and fuses and advancing the turn count. It starts out containing the following rules:
- turnEndSpacerRule (priority 10000): adds a blank line to the end of the previous command output.
- roomDaemonRule (priority 9000): executes the
roomDaemon()
method on the player character’s current room. - executeEventsRule (priority 8000): executes any pending Fuses or Daemons.
- advanceTurnCounterRule (priority 50): advances the turn counter.
To define additional rules to go into the
turnEndRules
rulebook, use the TurnEndRule
class.
A Note on Performance
Employing the sysrules extension in principle gives the library quite a
lot of extra work to do, since it has to run through a number of
rulebooks where it would otherwise have simply executed successive
statements in a method or function. This is unlikely to result in any
noticeable degradation of performance from the perspective of a player,
at least any player using a reasonably modern computer, but could
conceivably make test scripts run a little slower. It is completely
pointless to install this extension in your game unless you actually
intend making use of it by defining new rules or reordering existing
ones, since this would be to make the library do a whole lot of extra
work for nothing. It may also be overkill to use this extension to
achieve something that could be achieved more simply (or just as simply)
by some other means (such as defining an
InitObject
or using a
CustomMessages object
to change the prompt). On the other hand there is no reason why you
should not use this extension if you think it may prove useful or if you
want to experiment with it; in practice any slowdown in performance is
likely to be imperceptible (especially to players).
This covers most of what you need to know to use this extension. For additional information see the source code and comments in the systime.t file.
Adv3Lite Manual
Table of Contents |
Extensions >
Sysrules