A-code texts explainedA-code texts are de facto objects with their own methods, making them into a far more powerful and flexible game component than in any other IF system. Yet they can be treated also as simple text strings – the complexity of "text morphing" is there to be used, but it does not force itself onto game writers. This section explains A-code texts and their handling as of A-code version 12.91. Contents:General notes on style
A-code text basicsThis part of the section deals with the basics of A-code text declaration and use. A-code text declarationsAs of version 12, in-line texts are also permitted (see the in-line-text section), but traditionally A-code defines all its texts (other than place and objects descriptions) as separate named entities. The basic A-code text declaration has the form TEXT <text_name> <some_lines_of_text> Like all major A-code directives, the keyword TEXT must be at the very beginning of a line. The text lines following it must all have at least one leading blank. The declaration is terminated by a line with a non-blank first character (i.e. another major directive or a comment), or by end-of-file. While generally line breaks are ignored in the text definition (see the next section for more details), any texts declared using the TEXT directive are deemed to have a trailing end-of-line. A text fragment, without the trailing EOL can be declared using the FRAGMENT variant of a text declaration:
FRAGMENT <text_name> <some_lines_of_text> Here is a simple, purely artificial example: FRAGMENT LINE.START This line is sp TEXT LINE.END lit into two parts. When displayed by the A-code primitive SAY say line.start say line.end The result is a single line "This line is split into two parts." followed by a line break. Both TEXT and FRAGMENT directives can be used to declare "anonymous" texts by omitting the text name. This practice goes back to Dave Platt's original version and is now deprecated -- text switches can be used instead. See Appendix C on handling anonymous texts.
Basics of text definitionsA-code text definitions (the lines of text following the TEXT or FRAGMENT declaration line), are processed as follows:
In-line textsIn-line texts are accepted in all circumstances in which a reference to a named text is acceptable. The basic syntax is extremely simple. If a string beginning with a double quote is encountered where a text name would be appropriate, everything (line feeds included!) up to the next double quote is accepted as the desired text. E.g. say "This is a line of text, follwed by a blank line... followed by this line." The acdc translator in fact declares a standard A-code text, assigns an automatically generated name to it, and replaces the whole quoted string with that name. The upshot is that all text features described in this section apply to in-line texts as well. There is an additional syntax wrinkle to permit in-line texts to be text fragments and to permit them to have their own internal dynamics, as described in the dynamic implicit qualifiers section further in this section. If an in-line text starts with one of i, c, r or f, followed the colon character :, it gives the text a special property. If the character is f ("f:") then the text is deemed to be a fragment, without a trailing line feed. The other three possibilities declare the text dynamic method to be one of increment, cycle or random, as explained in the dynamic explicit qualifiers section below. Since fragment texts may also have their own dynamic methods, this additional syntax may be repeated as e.g. "f:c:" or "r:f:" etc. Centered textA text definition line beginning with the plus character + is treated as a line to be separately centered on the display. The plus sign is stripped off, and the line is prefixed with a line break (unless it happened to be preceded with one) and a line break is appended to it. The result is displayed centered. A-code also understands "block centering". A centered block is a set of successive text definition lines, each of which is prefixed with the equals character =. The equals signs are stripped off, and the whole block is displayed offset to the right in such a way that its longest line appears centered on the display. As with individual line centering, the block is prefixed by a line break if one is required (i.e. there wasn't one already), and suffixed with a line-break. Ends of line within the centered block are also honoured, contrary to the more general A-code convention. Here is an artificial example showing both kinds of centering: TEXT CENTERING.EXAMPLE This text shows +a centered line +and another centered line, as well as =a whole block =of lines, all of which =are centered as a single unit. Which is sometimes useful. If displayed (e.g.) in console (i.e. a fixed size font) on an 60-character wide display by say centering.example this would result in ------------------------------------------------------------ | This text shows | | a centered line | | and another centered line, | | as well as | | a whole block | | of lines, all of which | | are centered as a single unit. | | Which is sometimes useful. | ------------------------------------------------------------ Non-console mode (i.e. proportional font) displays achieve the same overall effect. Text post-processingA-code texts are not displayed to the player immediately. They are accumulated instead in an internal, dynamically sized buffer. The contents of this buffer are post-processed and displayed by the kernel when either a command prompt is issue to the player, or the game is about to exit. This permits some A-code directives to affect previously output text. For example the resay directive, instead of appending its text to the output buffer, first empties the buffer so that its text completely replaces anything accumulated so far. On the other hand the append directive preserves the accumulated text, but strips from it any terminating blank lines, effectively appending its text to the last output paragraph. The A-code kernel processes the accumulated buffer before displaying its contents, e.g. by normalising any successive blank lines to a single blank line. (Multiple blank lines may be magicked up by using the non-breaking space character – see the list of special characters in Appendix B. A-code text-morphing featuresThis part of the section describes the "text-morphing" features of A-code texts. Text switches definedA text switch is effectively an indexed array of strings embedded in a text message. More than one text switch can be embedded in a single text, and on the other hand, a whole text may consist of a single text switch. Formally, a text switch is a sequence of arbitrary text strings separated by forward slashes, with the whole sequence being enclosed in square brackets. E.g. [None/One/Two/Many] is an example of a simple text switch of four elements. The idea is that when the whole message is displayed to the player, just one element from every given switch is selected for display, according to some rule. The selection is made by using some "qualifier" value as the index of each text switch array encountered in the message. Text switch elements are indexed from zero upwards, so that in the above example the element None has the index value of zero, while the element Many has the index value of three. While ends of lines are treated in A-code text definitions as white spaces (just like, for example in HTML), an end of line immediately following a text switch separator character / is simply ignored, which conveniently allows text switches to be spread across several lines. E.g. the above shown switch could have been equivalently written as [None/ One/ Two/ Many] because A-code ignores any line-leading (and line-trailing) spaces in text definitions. Simple text switches, explicitly qualifiedThis text morphing feature was introduced by Dave Platt to handle messages such as the report of dwarf attacks on the player. Here's the definition of a text called knives.thrown and containing two text switches: TEXT KNIVES.THROWN [/One/Two/Three/Four/Five/Six/Seven/Many] nasty sharp kni[/fe is/ves are] thrown at you! This is displayed by an A-code text-handling language primitive SAY: say <text_name>, [<explicit_qualifier>] or in this specific case say knives.thrown, thrown The second parameter ("thrown") is in this case the name of a variable holding the number of knives thrown at the player, though from the point of view of the language syntax, it could be a constant, or any A-code entity possessing a value (e.g. an object or a location). In the absence of an implicit qualifier (to be explained later), this value is used to index any text switches embedded in the text supplied as the 1st parameter, where switch elements are counted from zero. So, if one knife is thrown, the displayed message will read: One nasty sharp knife is thrown at you! But suppose more than one knife is thrown, say 5 of them. The first switch is unproblematic -- its index values go from 0 to 8 -- but the second switch only has elements 0, 1 and 2. This is handled by the primary rule of text switches: use the index value nearest to the qualifier value. So the message becomes: Five nasty sharp knives are thrown at you! While in this example, the explicit qualifier was supplied as a variable, any A-code entity which has a value associated with it (e.g. an object, or a location, or a plain numeric constant) is also acceptable as an explicit qualifier. Repeated text switch elementsIn practice, text switch elements can be long, multi-line strings, and sometimes it is necessary to have some of the elements repeated. For example, if a player has a purse which can contain some coins, its contents could be described by the following text: TEXT PURSE.CONTAINS There [are/is/are] [no/one/two/three/several/several/several/several/ several/several/many] coin[s//s] in the purse. Such repetition is clearly wasteful, as well as tiresome, and the maintenance of switches with repeat elements is unnecessarily complex, because any changes to the repeated elements need to be applied to each of them. To get around this, A-code interprets a switch element consisting of the single character = to mean a repeat of the immediately preceding element. This in turn may have the same special format, in which case its preceding element is considered. For obvious reasons, the first (zero index) element of a switch may not be a repeat element. Using this convention, the above PURSE.CONTAINS text could be defined as TEXT PURSE.CONTAINS There [are/is/are] [no/one/two/three/several/ =/=/=/=/=/many] coin[s//s] in the purse. When qualified by an actual number of coins, this would specify the number as several for 4 to 9 coins and as many for any more than that. Word and value holdersA-code texts can also contain word and value place-holders, which get replaced dynamically at run-time by words or values specified by the explicit qualifier. Place-holders of either kind can occur both outside and inside text switches. The dollar sign $ is used as a value place-holder. If an unescaped dollar sign is encountered in a text to be displayed, and if the text is used with an explicit qualifier, the dollar is replaced with the numerical value of the qualifier. For example, the PURSE.CONTAINS text defined in the last section, could be re-defined as TEXT PURSE.CONTAINS There [are/is/are] [no/$] coin[s//s] in the purse. In this case, the A-code statement say purse.contains, 13 would result in the display of There are 13 coins in the purse. Of course, as before, any A-code value-bearing entity (variable, location, object...) could be used as a qualifier, instead of a constant value.
A word place-holder is signalled by an unescaped hash sign #, and is conceptually similar, except that a word, rather than a value, indicated by the explicit qualifier is inserted in place of the hash sign. Just what can be used as an explicit qualifier in this case is a bit more complicated. All declared vocabulary terms obviously have one (or more, if there are synonyms) actual word associated with them. At the author's discretion, most objects and possibly some locations will also have an associated vocabulary word or words. In all these cases there will be a "primary" word -- the first one declared in the list of synonyms (if there are any synonyms). It is this primary word that gets inserted in place of a word place-holder. All of this is easier to understand on some practical illustrations. Take for example the object chair1 with the associated nouns "chair" and "seat". Should there be a text declared as TEXT NO.KILL.THINGS The # is not something mortal, so cannot be killed! and an object declared as OBJECT -CHAIR1, CHAIR, SEAT where the name CHAIR1 is explicitly excluded from the player's vocabulary, then the statement say no.kill.things, chair1 would produce The chair is not something mortal, so cannot be killed! What makes this much more useful than may appear at the first glance, is the fact that A-code variables may store either values or pointers (references, to you Algol fans!) to arbitrary A-code entities. Hence if the game code executes somewhere the statement lda target, chair1 # Point variable TARGET at object CHAIR1 where "target" is a variable name, then a subsequent statement say no.kill.things, target will produce exactly the same display text as if the object "chair1" or just the simple noun "chair" were used as the explicit qualifier. Word place-holders really come into their own because the mandatory A-code variables ARG1 and ARG2 hold respectively the verb and the noun of the player's last command. So assuming that the player said "KILL CHAIR", then say no.kill.things, arg2 would once again tell the player that chairs are not for killing. However, there is a further subtlety here, when it comes to using player command words pointed at by the ARG1 and ARG2 variables, because in this case what is echoed as a part of the response is not necessarily the primary word associated with the referenced object, but the word actually used by the player (allowing for expansion of abbreviations, typo correction and vocabulary folding -- see the section on the full 3D structure of the A-code vocabulary). So if the player typed "KILL STO" (note the abbreviation of STOOL to STO, assumed to be unambiguous in this example), the displayed response would be The stool is not something mortal, so cannot be killed! Word place-holders can also appear both within and without text switches, but we'll cover that somewhat later on, under the heading of switch and holder interplay. Simple implicit qualifiersNot all texts need explicit qualification. For example, any texts associated with objects or locations (i.e. various forms of object and location descriptions) are deemed to be implicitly qualified by the current state of the object or location -- i.e. by its current internal value. Take for example the object BATTERIES defined as OBJECT BATTERIES, =BATTERY [/Fresh/Worn-out] batteries %[There is a pair of brand-new batteries in the goods tray./ There are fresh batteries here./ Some worn-out batteries have been discarded nearby.] &The two batteries are just the right size and shape for the lamp. Both are marked as "[BRAND-NEW/FRESH/WORN-OUT]" in chunky [blue/green/red] letters. which has the usual triplet of the inventory, ordinary and detailed descriptions. Each of these contains one or more text switches, which will get automatically qualified by the state of the object. So for instance, if the batteries are spent, the statement describe batteries will display The two batteries are just the right size and shape for the lamp. Both are marked as "WORN-OUT" in chunky red letters. Similarly the INVENTORY command and the general LOOK command will display their appropriate descriptions qualifying the embedded text switches by the object's state. It is important to note that implicit qualifiers always override any explicit qualifiers -- a point which will have a great significance in the next section. For now it is sufficient to observe that describe batteries, 0 would be completely pointless. Dynamic implicit qualifiersAs already noted elsewhere, A-code texts also carry an internal state (or value), initialised by default (like all A-code values) to zero. Note, however, that to avoid overriding explicit qualifiers in simple text switches, internal text states are only used as implicit qualifiers if a "method" for their manipulation is given as a part of the text definition. Formally, a full text definition looks like this: TEXT [<method>] <text_name> <line_of_text> [....] where <method> is one of increment, cycle, random and assigned. All of these have different effects.
It should be noted that none of the above described methods, nor the "null" non-method (i.e. when no method is specified) preclude the state value to be assigned into a text or numerically manipulated and examined like any ordinary variable. So for example, taking the above defined DIGITS text, the statements say digits # Internal state starts from 0! add digits, 10 # It got incremented to 1, will now be 11 say digits # It's now 11 will be reset back to zero say digits would result in "1 1 1", "2 3 4", "1 1 1" In-line texts can also have dynamic implicit qualifiers. If such a text starts with the colon character ':', followed by a letter, followed by another colon, these three characters are stripped off and a dynamic method is associated with the in-line text in question on the basis of the letter between the two colons: i for increment, c for cycle, r for random. Note that the absence of the assigned pseudo-method, on the grounds that there is no way for an in-line text to be referred to by the rest of the A-code source. Switch and holder interplayNow that we have been through the details of implicit qualifiers, the interplay between text switches and place-holders can be stated very simply as two rules:
This enables responses such as exemplified by this Adv770 text: TEXT cycle ITS.JUST.A It's just a #. [Nothing very remarkable about it/Not remarkable in any way/Nothing special to it/The apt description is "unremarkable"]. The typical use for this text is say its.just.a, arg2 which will replace the word place-holder in the text with the noun from the player's last command, while cycling in its successive uses through its embedded text switch. It may at first appear strange that implicit qualifiers are ignored in place-holder substitution, while explicit qualifiers are overridden by implicit ones for switch processing. However, this arrangement does exactly what is often wanted, because it makes it much more sensible to have place-holders within text switches. Again, an example from Adv770 is probably a good illustration: TEXT cycle NOCOMPRENDE.VERB [My ignorance shames me, but I do not know what action might be signified by "#"./Alas, my vocabulary is too limited to encompass "#". Try some other verb?/Very remiss of me to be sure, but I've never learned to "#"./ To my shame, I have no idea what you mean by "#"./"#"? Sorry, I don't what it means./I am afraid "#" is not a verb I've ever learned./Ahem... "#" is not in my dictionary. Would you care to re-phrase?/Regrettably, that is not something I know how to do.] If the game fails to make sense of the player's verb, all it has to do now is say nocomprende.verb, arg1 thereby producing a wide variety of responses. As an aside, in practice I find that the cycle method is much more useful for this purpose than the random one. Contrary to our intuitive expectation, randomness tends to be non-uniform (i.e. "clumpy") and hence requires a large number of options, if obvious repetitions are to be avoided. Text tyingAs already noted, A-code variables can be in fact pointers to other A-code entities. The same is true for internal values of A-code texts. A text can be "tied" to another value-bearing entity, thereby removing the need for the game code to explicitly ensure the text value stays in sync with the state of that other entity. In effect, tying texts to other entities is also a text "method" in that it activates the text's implicit qualifier (which in this case happens to be the value of the entity to which the text is tied). Because this additional text method is not purely internal to the text, there may be reasons for the tying to be performed dynamically within the game code. Hence tying is performed by means of an executable language statement, rather than in text declaration. Once again, an example may be of help. Adv770 has a quartz seal, which can have one of two states. If the player tries to examine the seal when it is not in his inventory, the game tells him the seal is too small and needs to be picked up. The actual wording of this admonition depends on the state of the seal, hence the game initialisation code contains the statement tie pick.up.seal, seal This effectively ties the value of the text PICK.UP.SEAL to the value of the object SEAL. From now on, if the state of the object changes, the message displayed by say pick.up.seal will automatically change to match, because PICK.UP.SEAL contains a two-component text switch. Yes, in this particular case one could use SEAL as an explicit qualifier: say pick.up.seal, seal The seal example merely illustrates the technique. It is too simple to show the justification of that technique. The actual motivation for introducing text tying in A-code was provided by my efforts to resolve a highly complex problem of constructing a location description which depended on several independent factors. It is far too complex to be presented as an illustrative example. Text nestingSometimes a number of separate messages (e.g. location descriptions) consist of a part which is common to them all, and another part which is specific to a given message (or group of messages). This can present maintenance problems (e.g. fixing a typo in all identical parts) and is also wasteful. A-code offers an alternative approach. The common message part can be defined as a separate text fragment (i.e. a text without a trailing end-of-line character), which can be "nested" within individual messages.
An A-code message text may include the symbolic name of another text
enclosed in unescaped braces (i.e. curly brackets): { Here's an example from Adv770, showing the declarations of the first six of the Adv550's ice tunnels. It uses double-level nesting. FRAGMENT INTRICATE.TUNNELS You are in an intricate network of ice tunnels. # FRAGMENT ICE.DEAD.END {INTRICATE.TUNNELS} The only exit is # FRAGMENT ICE.TUNNELS {INTRICATE.TUNNELS} Exits lead # PLACE ICE.CAVE.1 {ICE.TUNNELS} north and west. # PLACE ICE.CAVE.1A {ICE.DEAD.END} south. # PLACE ICE.CAVE.2 {ICE.TUNNELS} north, east and west. # PLACE ICE.CAVE.2A {ICE.TUNNELS} north and south. # PLACE ICE.CAVE.3 {ICE.TUNNELS} north and east. # PLACE ICE.CAVE.3A {ICE.DEAD.END} south. Nested texts may have their own implicit qualifiers, of course. If the top level text is used with an explicit qualifier, this is passed on as an explicit qualifier to all nested texts, to any depth. As a special case, A-code also permits nesting of the ARG1 and ARG2 variables, which are treated as the words actually typed by the player (possibly expanded from an abbreviated state, and typo-corrected). This is used, for example, as follows: TEXT YOU.DO.IT You {ARG1} the {ARG2}. If the player typed (e.g.) "G LAMP", and succeeded in picking up the lamp, say you.do.it would display "You get the lamp." Appendix A: A-code text-handling primitivesAppendix B: Special characters in text definitionsThe following characters are have special meaning in text definitions, and have to be escaped with a reverse slash if they are to be displayed "raw".
Appendix C: Handling anonymous texts (deprecated!)Anonymous texts can be only handled through pointers. Count text declarations backwards from the anonymous text in question, until you come to a named text. The resulting count is the anonymous text's offset from that named text. Point a variable at that named text and then add the offset to the variable. The variable now points at your anonymous text and all A-code primitives which handle automatic indirection, will accept the variable as a reference to the anonymous text. Here's an example in the shape of a complete A-code test program: style A-code 12 TEXT FIRST.TEXT First text TEXT Second text TEXT Third text init local ptr lda ptr, first.text add ptr,2 say ptr stop repeat It will print "Third text" and then stop. (NB: The repeat section is null, but the translator will complain if it is absent.)
|