Contents this "Lesson:"
| Become thoroughly conversant with grammar.h. |
Verb 'climb' 'scale'
* noun -> Climb
* 'up'/'over' noun -> Climb;
Verb
defines a verb.
Climb
is the verb and
scale
is its synonym. Alternate
prepositions
can be listed together when separated by a forward slash.
Noun
is
the verb's object.
Climb
(after the ->) is the verb routine that will be executed when the climb
command is entered by the player. That's pretty straight forward.
| Verb routines are always followed by "Sub" in code, but when part of a grammar declaration, such as in grammar.h, the Sub suffix is left off. Bit strange, but that is how it is. |
Verb 'clamber' = 'climb';Creating Verb Routines
[BurnSub; L__M(##Burn,1,noun); ]; <- The original BurnSub in verblibm.h. Burn: "This dangerous act would achieve little." <- The burn message in english.h.Well, first BurnSub needs to be replaced. It can be replaced by including in your code (before parser.h):
Replace BurnSub;Now that it has been replaced, a new Burnsub routine needs to be provided.
[ BurnSub;
if ((match notin player) || (match in player && match hasnt light))
"You have no source of flame.";
if (noun has animate)
print_ret "I don't suppose ", (the) noun, " would care for that.";
if (noun has light)
print_ret (The) noun, " is already burning.";
if (noun has is_paper)
{ remove noun;
print_ret (The) noun, " catches fire and burns down to nothing.";
}
"That dangerous act would achieve little.";
];
A Doe Crib Note:
| Actually including burn in a game is not recommended, because it requires a lot of coding. |
Object bonfire "bonfire"
with name "bonfire" "fire",
description
[; if (self has light) "The bonfire burns merrily away.";
"Someone has stacked a bonfire here, ready to be burned.";
],
before
[; Burn : If (self hasnt light)
{ if ((match in player) && (match has light))
{ give self light;
"You touch the match to the bonfire and flames leap up.";
}
}
];
Of course, it would probably also use a timer to burn the bonfire down to
ashes.
Object man "man"
has animate male
with name "man",
life [; etc.; ];
Object chair "chair"
has supporter enterable
with name "chair" "plastic" "ordinary",
description "It's an ordinary, plastic chair.",
before
[; Enter : if (man in chair)
"It seems to be occupied.";
];
Object chair "chair"
has supporter enterable
with name "chair" "plastic" "ordinary",
description "It's an ordinary, plastic chair.",
before
[; Enter : if (man in chair)
"It seems to be occupied.";
Burn : print "Plastic really smells when it burns. ";
];
[ BurnSub;
if ((match notin player) || (match in player && match hasnt light))
"You have no source of flame.";
if (noun has animate)
print_ret "I don't suppose ", (the) noun, " would care for that.";
if (noun has light)
print_ret (The) noun, " is already burning.";
if (noun has is_paper)
{ remove noun;
print_ret (The) noun, " catches fire and burns down to nothing.";
}
"That dangerous act would achieve little.";
];
Object bonfire "bonfire"
with name "bonfire" "fire",
description
[; if (self has light) "The bonfire burns merrily away.";
"Someone has stacked a bonfire here, ready to be burned.";
],
before
[; Burn : If (self hasnt light)
{ if ((match in player) && (match has light))
{ give self light;
"You touch the match to the bonfire and flames leap up.";
}
}
];
The bonfire is only lit, returning true (1) and skipping BurnSub, if it isn't
already
burning
and
the player has a lit match. If it is already burning, its before routine
returns 0, which calls BurnSub to print "The bonfire is already burning." If
the player doesn't have a match or has a match that isn't lit, its before
routine also returns 0, which calls BurnSub to print "You have no source of
flame."
| Conditions you want specific to an object (in this case, the bonfire already lit and a lit match in player's possession) have to be tested in that object's before routine itself, because the corresponding verbsub will only be executed afterward (if at all). |
| Command | Example |
|---|---|
|
Verb
(creates a new verb) |
Verb 'xyzzy'
* -> XYZZY;
|
|
=
(creates a synonym) |
Verb 'clamber' = 'climb'; |
|
Extend
(adds to the definition of an existing verb in grammar.h, new addition is placed last in the list) |
Extend 'climb' * 'up'/'down' noun -> Climb; |
|
Extend verb last
(adds to the definition of an existing verb, new addition is placed at the end of the list, same as above) |
Extend 'climb' last * 'up'/'down' noun -> Climb; |
|
Extend verb first
(adds to the definition of an existing verb, new addition is placed at the front of the list) |
Extend 'climb' first * 'up'/'down' noun -> Climb; |
|
Extend verb replace
(completely replaces the default definition in grammar.h) |
Extend 'climb' replace * 'up'/'down' noun -> Climb; |
| Inform does not support multi-dimensional arrays, which is one of its biggest drawbacks and why creating a database in Inform is almost impossible. |
Object hidden_door "hidden door" has door openable static with name "hidden" "door", found_in secret_passage library;Hidden door's found_in property is a two element array: secret_passage is element 0, and library is element 1. The word, array, is not included in a property array, so the only way you can tell that it is an array is by the multiple number of elements. (Yes, name is also a property array, but a special one.)
Trying to access non-existent array elements, going outside array bounds, is
probably the second biggest cause of vile zero errors from hell (see
Attributes, #4.)
Array test_array ->10; (declaration) test_array->10 = 1; (out of bounds, remember it would be 0-9) |
Object nowhere; found_in secret_passage nowhere;Now the door will only appear in the secret_passage and not in the library until the right conditions are met.
Object hidden_door "hidden door"
has door openable static
with name "hidden" "door",
when_open
[; if (location==secret_passage) "The hidden door opens west.";
"The bookcase is open, revealing a secret passage east.";
],
when_closed "The hidden door is closed.",
door_dir
[; if (location==secret_passage) return w_to;
return e_to;
],
door_to
[; if (location==secret_passage) return library;
return secret_passage;
],
before
[; Close : if (location == library)
"It doesn't move, you'll have to close it some
other way.";
],
found_in secret_passage nowhere;
Object nowhere;
Object secret_passage "Passage"
has light
with description "It is hard to see in here.",
w_to hidden_door,
cant_go "You can't see where the passage leads to.";
Object library "Library"
has light
with description "You are in a big library with a bookcase crammed
full of books.",
e_to
[; if (self hasnt general) "You can't go that way.";
return hidden_door;
];
Object -> shelves "bookcase"
has scenery
with name "shelves" "bookcase" "bookshelves" "case", article "some",
description "There are a lot of books, but one big red book
is especially noticeable.";
Object -> big_book "big book"
has scenery
with name "book" "big" "red",
description "The red book is unusually big. It appears as if the title
was painted on.",
before
[; Take : "It moves a little, but otherwise seems attached to
the bookcase.";
Push, Turn : "It doesn't budge.";
Pull : print "You pull the book. ";
if (library has general)
{ (hidden_door.&found_in)-->1 = nowhere;
give hidden_door ~open;
give library ~general;
MoveFloatingObjects();
"^^~Whoosh!~ The bookcase swings closed.";
}
(hidden_door.&found_in)-->1 = library;
give hidden_door open;
give library general;
MoveFloatingObjects();
"^^~Whoosh!~ The bookcase swings open revealing a
secret passage to the east.";
];
MoveFloatingObjects
is a built-in Inform library routine that is automatically called at the
beginning of each turn to adjust objects that can be found_in
more than one location. But if you change found_in directly,
MoveFloatingObjects needs to be called immediately to make the necessary
adjustments. It is the routine that will actually move a found_in to the right
locations, in this case, the
hidden door to/from the library.
Option Hint1 "How do I get the kitten out of the tree?"
class Hintobj
with the_hints "Try chopping down the tree."
"Too destructive? Try a garden hose."
"No hose? Read raif for other ways to get the
kitten down.";
the_hints is a string array. Notice, as with found_in, there is no comma
between the elements.
class Hintobj
with hints_seen 0,
hint_done 0,
the_hints "Put list of hints here (in the object of this class).",
description
[; self.showhints(); rtrue;
],
showhints
[ max i k;
max = (self.#the_hints / 2);
for (i = 1: (i <= max): i++)
{ if ((i==1) && ((max > 1) && (self.hints_seen < max)))
print "There are ", (LanguageNumber) max, " increasingly clear \
hints. Press H for another hint, any other key to quit.^^^";
print "(", i, "/", max, ") ";
print (string) (self.&the_hints-->(i-1));
new_line; new_line;
! How many times through this loop? That is the number of the current
! hint. Starting with 1 = 0 hints_seen.
if (self.hints_seen == (i-1)) self.hints_seen = self.hints_seen + 1;
! Only call read_char if currently on the last hint seen and
! skip on the very last hint.
if ((i < max) && (i == self.hints_seen))
{ @read_char 1 0 0 k;
if (k ~= 'H' or 'h') return;
}
}
];
max uses the # operator (divided by two, that confusing byte/word thing again)
to find the number of elements in each hint object's the_hints array.
self.&the_hints--> uses (i-1) because the for loop starts at 1 instead of 0
(since I find it easier to think of 1-10 than 0-9, as many do). Each time a new
hint is read, the hints_seen property is incremented by 1, so that the next time
previously seen hints will automatically show on the screen. (Note that regular
Inform uses two-byte words, and Glulx Inform uses four-byte words, so in
regular Inform you divide by two and in Glulx Inform you divide by four.)
| Command | Examples |
|---|---|
|
Array
(declares an array) |
Array test_array-->5; Array test_array-->1 2 3 4 5; Array test_array string 5; Array test_array->"Name"; Array test_array->'N' 'a' 'm' 'e'; Array test_array table 5; Array test_array table 'name1' 'name2' 'name3' 'name4' 'name5'; Array test_array table [; "Name One"; "Name Two"; "Name Three"; "Name Four"; "Name Five"; ]; |
|
array types:
-> (byte) --> (word) string (array of characters, bytes) table (array of strings, words) |
see above |
| Command | Example |
|---|---|
|
#
(returns the length of an array stored in an object's property) |
len = (obj.#property_array) / 2; |
|
&
(accesses an array element stored in an object's property) |
if (obj.&property_array-->0 == 1); |