RTP contents
Reporting.
All RTPs are produced by calling the following routine, which takes one compulsory argument: n, the RTP number. When I7 is being used with the Inform user interface, it's important that these numbers correspond to the explanatory web pages stored within the interface application: those in turn are created by the inrtps utility. The interface knows to display these pages because it parses the printed output during play to look for the text layout produced by the following routine: so do not reformat RTPs without ensuring that corresponding changes have been made to the Inform user interface applications.
The arguments par1, par2 and par3 are optional parameters clarifying the message; ln is an optional parameter specifying a paragraph number in the original source text (though in fact I7 does not use this at present).
24Array RTP_Buffer --> 7;
25[ RunTimeProblem n par1 par2 par3 ln file;
26 if (RTP_Buffer-->0 == -1) {
27 RTP_Buffer-->0 = n;
28 RTP_Buffer-->1 = par1;
29 RTP_Buffer-->2 = par2;
30 RTP_Buffer-->3 = par3;
31 RTP_Buffer-->4 = ln;
32 RTP_Buffer-->5 = file;
33 }
34 RunTimeProblemShow();
35];
36[ ClearRTP;
37 RTP_Buffer-->0 = -1;
38 RTP_Buffer-->6 = false;
39];
40[ SuspendRTP;
41 RTP_Buffer-->6 = true;
42];
43[ ResumeRTP;
44 RTP_Buffer-->6 = false;
45];
46[ RunTimeProblemShow n par1 par2 par3 ln file i c;
47 if (RTP_Buffer-->0 == -1 or -2) return;
48 if (RTP_Buffer-->6) return;
49
50 n = RTP_Buffer-->0;
51 par1 = RTP_Buffer-->1;
52 par2 = RTP_Buffer-->2;
53 par3 = RTP_Buffer-->3;
54 ln = RTP_Buffer-->4;
55 file = RTP_Buffer-->5;
56 RTP_Buffer-->0 = -2;
57
58 print "^*** Run-time problem P", n;
59 if (ln) {
60 print " (at paragraph ", ln, " in ";
61 if (file == 0) print "the source text";
62 else ShowOneExtension(file);
63 print ")";
64 }
65 print ": ";
66 switch(n) {
67 RTP_BACKDROP:
68 print "Tried to move ", (the) par1, " (a backdrop) to ", (the) par2,
69 ", which is not a region.^";
70 RTP_CANTCHANGE:
71 print "Tried to change player to ", (the) par1, ", which is not a person.^";
72 RTP_NOEXIT:
73 print "Tried to change ", (the) par2, " exit of ", (the) par1,
74 ", but it didnt seem to have such an exit to change.^";
75 RTP_EXITDOOR:
76 print "Tried to change ", (the) par2, " exit of ", (the) par1,
77 ", but it led to a door, not a room.^";
78 RTP_IMPREL:
79 print "Tried to access an inappropriate relation for ", (the) par1,
80 ", violating ", (string) RlnGetF(par2, RR_DESCRIPTION), ".^";
81 RTP_TOOMANYRULEBOOKS:
82 print "Too many rulebooks in simultaneous use.^";
83 RTP_TOOMANYEVENTS:
84 print "Too many timed events are going on at once.^";
85 RTP_BADPROPERTY:
86 print "Tried to access non-existent property for ", (the) par1, ".^";
87 RTP_UNPROVIDED:
88 print "Since ", (the) par1, " is not allowed the property ~",
89 (string) par2, "~, it is against the rules to try to use it.^";
90 RTP_UNSET:
91 print "Although ", (the) par1, " is allowed to have the property ~",
92 (string) par2, "~, no value was ever given, so it cant now be used.^";
93 RTP_TOOMANYACTS:
94 print "Too many activities are going on at once.^";
95 RTP_CANTABANDON:
96 print "Tried to abandon an activity which wasnt going on.^";
97 RTP_CANTEND:
98 print "Tried to end an activity which wasnt going on.^";
99 RTP_CANTMOVENOTHING:
100 print "You cant move nothing.^";
101 RTP_CANTREMOVENOTHING:
102 print "You cant remove nothing from play.^";
103 RTP_DIVZERO:
104 print "You cant divide by zero.^";
105 RTP_BADVALUEPROPERTY:
106 print "Tried to access property for a value which didnt fit: ",
107 "if this were a number it would be ", par1, ".^";
108 RTP_NOTBACKDROP:
109 print "Tried to move ", (the) par1, " (not a backdrop) to ", (the) par2,
110 ", which is a region.^";
111 RTP_TABLE_NOCOL:
112 print "Attempt to look up a non-existent column in the table ",
113 (PrintTableName) par1, ".^";
114 RTP_TABLE_NOCORR:
115 print "Attempt to look up a non-existent correspondence in the table ",
116 (PrintTableName) par1, ".^";
117 RTP_TABLE_NOROW:
118 print "Attempt to look up a non-existent row in the table ",
119 (PrintTableName) par1, ".^";
120 RTP_TABLE_NOENTRY:
121 print "Attempt to look up a non-existent entry at column ", par2,
122 ", row ", par3, " of the table ", (PrintTableName) par1, ".^";
123 RTP_TABLE_NOTABLE:
124 print "Attempt to blank out a row from a non-existent table (value ",
125 par1, ").^";
126 RTP_TABLE_NOTABLE2:
127 print "Attempt to access an entry from a non-existent table.^";
128 RTP_TABLE_NOMOREBLANKS:
129 print "Attempt to choose a blank row in a table with none left: table ",
130 (PrintTableName) par1, ".^";
131 RTP_TABLE_NOROWS:
132 print "Attempt to choose a random row in an entirely blank table: table ",
133 (PrintTableName) par1, ".^";
134 RTP_TABLE_CANTRUNTHROUGH:
135 print "Attempt to repeat through a table in a tricky column order: table ",
136 (PrintTableName) par1, ".^";
137 RTP_TABLE_CANTSORT:
138 print "Attempt to sort a table whose ordering must remain fixed: table ",
139 (PrintTableName) par1, ".^";
140 RTP_TABLE_CANTSAVE:
141 print "Attempt to save a table to a file whose data is unstable: table ",
142 (PrintTableName) par1, ".^";
143 RTP_TABLE_WONTFIT:
144 print "File being read has too many rows or columns to fit into table: table ",
145 (PrintTableName) par1, ".^";
146 RTP_TABLE_BADFILE:
147 print "File being read is not a previously saved table: table ",
148 (PrintTableName) par1, ".^";
149 RTP_NOTINAROOM:
150 print "Attempt to test if the current location is ",
151 (the) par1, ", which is not a room or region.^";
152 RTP_BADTOPIC:
153 print "Attempt to see if a snippet of text matches something which
154 is not a topic.^";
155 RTP_ROUTELESS:
156 print "Attempt to find route or count steps through an implicit
157 relation.^";
158 RTP_PROPOFNOTHING:
159 print "Attempt to use a property of the nothing non-object: property ",
160 (PrintPropertyName) par2, "^";
161 RTP_DECIDEONWRONGKIND:
162 print "Attempt to decide on V where V is the wrong kind of object.^";
163 RTP_DECIDEONNOTHING:
164 print "Attempt to decide on nothing.^";
165 RTP_LOWLEVELERROR:
166 print "Low level error.^";
167 RTP_DONTIGNORETURNSEQUENCE:
168 print "Attempt to ignore the turn sequence rules.^";
169 RTP_SAYINVALIDSNIPPET:
170 print "Attempt to say a snippet value which is currently invalid: words ",
171 par1, " to ", par2, ".^";
172 RTP_SPLICEINVALIDSNIPPET:
173 print "Attempt to splice a snippet value which is currently invalid: words ",
174 par1, " to ", par2, ".^";
175 RTP_INCLUDEINVALIDSNIPPET:
176 print "Attempt to match a snippet value which is currently invalid: words ",
177 par1, " to ", par2, ".^";
178 RTP_LISTWRITERMEMORY:
179 print "The list-writer has run out of memory.^";
180 RTP_CANTREMOVEPLAYER:
181 print "Attempt to remove the player from play.^";
182 RTP_CANTBEOFFSTAGE:
183 print "Attempt to move the player off-stage.^";
184 RTP_CANTREMOVEDOORS:
185 print "Attempt to remove a door from play.^";
186 RTP_CANTCHANGEOFFSTAGE:
187 print "Attempt to change the player to a person off-stage.^";
188 RTP_MSTACKMEMORY:
189 print "The memory stack is exhausted.^";
190 RTP_TYPECHECK:
191 print "Phrase applied to an incompatible kind of value.^";
192 RTP_FILEIOERROR:
193 print "Error handling external file.^";
194 RTP_HEAPERROR:
195 print "Memory allocation proved impossible.^";
196 RTP_LISTRANGEERROR:
197 print "Attempt to use list item which does not exist.^";
198 RTP_LISTSIZENEGATIVE:
199 print "Attempt to resize list to ", par1, " entries - there must ",
200 "always be 0 or more.^";
201 RTP_REGEXPSYNTAXERROR:
202 print "Syntax error in regular expression.^";
203 RTP_NOGLULXUNICODE:
204 print "This interpreter does not support Unicode.^";
205 RTP_BACKDROPONLY:
206 print "Only backdrops can be moved to multiple places.^";
207 RTP_NOTTHING:
208 print "Tried to move ", (the) par1, " (not a thing) to ", (the) par2,
209 ", but only things can move around.^";
210 RTP_SCENEHASNTSTARTED:
211 print "The scene ", (PrintSceneName) par1,
212 " hasnt started, so you cant ask when it did.^";
213 RTP_SCENEHASNTENDED:
214 print "The scene ", (PrintSceneName) par1,
215 " hasnt ended, so you cant ask when it did.^";
216 RTP_NEGATIVEROOT:
217 print "You cant take the square root of a negative number.^";
218 RTP_CANTITERATE:
219 print "You cant implicitly repeat through the values of this kind: ",
220 "a problem arising from a description which started out here - ~",
221 (string) par1, "~.^";
222 RTP_WRONGASSIGNEDKIND:
223 print "Attempt to set a variable to the wrong kind of object: ",
224 "you wrote ", (string) par2, ", which sets the value to ", (the) par1,
225 " - but that doesnt have the kind ", (string) par3, ".^";
226 RTP_RELKINDVIOLATION:
227 print "Tried to change a relation for objects with the wrong kinds: ",
228 (string) RlnGetF(par3, RR_DESCRIPTION), ", but you tried to ",
229 "relate (or unrelate) ", (the) par1, " to ", (the) par2, ".^";
230 RTP_CANTMAKEPART:
231 print "Tried to make the player part of something: ",
232 (the) par1, ".^";
233 RTP_TEXTTOKENTOOHARD:
234 print "This use of [text] is too complicated.^";
235 RTP_RELATIONCHANGEIMPOSSIBLE:
236 print "This change of the relations nature is impossible in play.^";
237 RTP_RELMINIMAL:
238 print "This operation cant be done with the relation ",
239 (string) RlnGetF(par3, RR_DESCRIPTION), ".^";
240 RTP_REGIONSNOTADJACENT:
241 print "You cant test whether something is adjacent to a region: ",
242 "such as, in this case, ", (the) par1, ".^";
243 }
244 print "^";
245];
Low-Level Errors.
The following is a residue from the old I6 library, and most of its possible messages can't be seen in I7 use – for instance I7 has no timers or daemons, so error 4 is out of the question. But we retain the routine because the veneer code added by I6 requires it to be present.
254Constant MAX_TIMERS = 0;
255[ RunTimeError n p1 p2;
256 #Ifdef DEBUG;
257 print "** Library error ", n, " (", p1, ",", p2, ") **^** ";
258 switch (n) {
259 1: print "preposition not found (this should not occur)";
260 2: print "Property value not routine or string: ~", (property) p2, "~ of ~", (name) p1,
261 "~ (", p1, ")";
262 3: print "Entry in property list not routine or string: ~", (property) p2, "~ list of ~",
263 (name) p1, "~ (", p1, ")";
264 4: print "Too many timers/daemons are active simultaneously.
265 The limit is the library constant MAX_TIMERS (currently ",
266 MAX_TIMERS, ") and should be increased";
267 5: print "Object ~", (name) p1, "~ has no ~time_left~ property";
268 7: print "The object ~", (name) p1, "~ can only be used as a player object if it has
269 the ~number~ property";
270 8: print "Attempt to take random entry from an empty table array";
271 9: print p1, " is not a valid direction property number";
272 10: print "The player-object is outside the object tree";
273 11: print "The room ~", (name) p1, "~ has no ~description~ property";
274 12: print "Tried to set a non-existent pronoun using SetPronoun";
275 13: print "A topic token can only be followed by a preposition";
276 default: print "(unexplained)";
277 }
278 print " **^";
279 #Ifnot;
280 print "** Library error ", n, " (", p1, ",", p2, ") **^";
281 #Endif;
282 RunTimeProblem(RTP_LOWLEVELERROR);
283];
Argument Type Checking Failed.
This is called when run-time type checking for the argument of a phrase fails, so that no definition for the phrase can be applied.
290[ ArgumentTypeFailed line file;
291 RunTimeProblem(RTP_TYPECHECK, 0, 0, 0, line, file);
292];
Return Type Checking Failed.
Similarly, though in a more restricted set of circumstances. NI can usually prove that the value returned by a phrase matches its supposed kind, but because of the deliberately weak type-checking within the kind of value "object" it will allow a broader kind of object to be returned when the requirement is for a narrower one. In such cases, it checks the result with the following routine. The value V is a valid "object", but that means it can be nothing, and we must check that it matches a given I7 kind K (where nothing is not valid).
305[ CheckKindReturned V K;
306 if (V ofclass K) return V;
307 if (v == nothing) RunTimeProblem(RTP_DECIDEONNOTHING);
308 else RunTimeProblem(RTP_DECIDEONWRONGKIND);
309 return V;
310];
Whether Provides.
This routine defines the phrase "if O provides P": there are three tests to pass, and if any of the three fail, we return false. (The issue_rtp flag, causing RTPs to be issued depending on which test fails, is never set when the routine is simply testing the condition.)
Firstly, P has to be a property known to I7. Secondly, there has to be permission either for this individual object to have it, or for its kind to have it, or its kind's kind, and so on. Thirdly, the object has to actually have the property in question at an I6 level – having permission to have it doesn't mean it actually does have.
325[ WhetherProvides obj either_or p issue_rtp off i textual a l;
326 if (metaclass(obj) ~= Object) rfalse;
327 if (p<0) p = ~p;
328 if (either_or) {
329 if (p < FBNA_PROP_NUMBER) off = attributed_property_offsets-->p;
330 else off = valued_property_offsets-->p;
331 } else off = valued_property_offsets-->p;
332 if (off<0) {
333 if (issue_rtp) RunTimeProblem(RTP_BADPROPERTY, obj);
334 rfalse;
335 }
336 textual = property_metadata-->off; off++;
337
338 if (ScanPropertyMetadata(obj, off)) jump PermissionFound;
339 if (obj provides KD_Count) {
340 l = obj.KD_Count;
341 while (l > 0) {
342 a = l*2;
343 if (ScanPropertyMetadata(KindHierarchy-->a, off)) jump PermissionFound;
344 l = KindHierarchy-->(a+1);
345 }
346 }
347 if (issue_rtp) RunTimeProblem(RTP_UNPROVIDED, obj, textual);
348 rfalse;
349
350 .PermissionFound;
351 if (either_or) rtrue;
352 if (obj provides p) rtrue;
353 if (issue_rtp) RunTimeProblem(RTP_UNSET, obj, textual);
354 rfalse;
355];
356
357[ PrintPropertyName p off textual;
358 if (p<0) p = ~p;
359 off = valued_property_offsets-->p;
360 textual = property_metadata-->off;
361 print (string) textual;
362];
The property_metadata table is a series of zero-terminated lists of objects (or class objects, representing I7 kinds). Each list corresponds to a single property; the position in the table is called the "offset" for the property. The following searches from a given offset.
371[ ScanPropertyMetadata obj off i;
372 for (i=off: property_metadata-->i >= 0: i++)
373 if (obj == property_metadata-->i) rtrue;
374 rfalse;
375];
Get Either-Or Property.
If p represents a property, then ~p (its bitwise negation) represents its logical negation. The bitwise negation will change the sign bit; since all properties are ordinarily positive, we can detect bitwise negation by looking for negative property numbers. (This could fail on Glulx story files above 1GB in size, but that's about 1000 times the size of the record-sized file created thus far.)
FBNA stands for First Boolean Not to be an Attribute, in the I6 sense. Some either/or (i.e., boolean) properties are stored as I6 attributes, others as I6 properties whose values are always true or false.
Note that we allow either/or properties to be read for any object, regardless of permissions, returning false if the object does not have the property. This is so that a description such as "open things" can be applied against any object without run-time errors, even though it is only normally valid for doors and containers.
396[ GetEitherOrProperty o p;
397 if (o == nothing) rfalse;
398 if (p<0) p = ~p;
399 if (WhetherProvides(o, true, p, false)) {
400 if (p<FBNA_PROP_NUMBER) { if (o has p) rtrue; rfalse; }
401 if ((o provides p) && (o.p)) rtrue;
402 }
403 rfalse;
404];
Set Either-Or Property.
An attempt to write an either/or property which is not provided will, however, always produce a run-time problem.
411[ SetEitherOrProperty o p negate adj;
412 if (p<0) { p = ~p; negate = ~negate; }
413 if (adj) {
414 (adj)(o);
415 } else if (WhetherProvides(o, true, p, true)) {
416 if (negate) {
417 if (p<FBNA_PROP_NUMBER) give o ~p; else o.p = false;
418 } else {
419 if (p<FBNA_PROP_NUMBER) give o p; else o.p = true;
420 }
421 }
422];
Value Property.
Some value properties belong to other values (those created in tables), and these are detected by being properties of the special ValuePropertyHolder pseudo-object – an I6 object which is not part of the world model, and not a valid I7 "object" value, but which is used in order that properties belonging to values are still I6 property numbers. ValuePropertyHolder.P is the table column address for this property; obj is then a value for the kind of value created by the table, so it is used as an index into the table column to get the address of the memory location storing the property value.
The door_to property, relevant only for doors, is called rather than read: this enables it to be an I6 routine returning the other side of the door from the one which the player is on.
440[ GProperty K V pr obj;
441 if (K == OBJECT_TY) obj = V; else obj = KOV_representatives-->K;
442 if (obj == 0) { RunTimeProblem(RTP_PROPOFNOTHING, obj, pr); rfalse; }
443 if (obj provides pr) {
444 if (K == OBJECT_TY) {
445 if (pr == door_to) return obj.pr();
446 if (WhetherProvides(V, false, pr, true)) return obj.pr;
447 rfalse;
448 }
449 if (obj ofclass K0_kind)
450 WhetherProvides(V, false, pr, true);
451 if ((V < 1) || (V > obj.value_range)) {
452 RunTimeProblem(RTP_BADVALUEPROPERTY); return 0; }
453 return (obj.pr)-->(V+COL_HSIZE);
454 } else {
455 if (obj ofclass K0_kind)
456 WhetherProvides(V, false, pr, true);
457 }
458 rfalse;
459];
Write Value Property.
This routine's name must consist of the read-value-property routine's name with the prefix Write, as that is how a reference to such a property is converted from an rvalue to an lvalue.
467[ WriteGProperty K V pr val obj;
468 if (K == OBJECT_TY) obj = V; else obj = KOV_representatives-->K;
469 if (obj == 0) { RunTimeProblem(RTP_PROPOFNOTHING, obj, pr); rfalse; }
470 if (K == OBJECT_TY) {
471 if (WhetherProvides(V, false, pr, true)) obj.pr = val;
472 } else {
473 if ((V < 1) || (V > obj.value_range))
474 return RunTimeProblem(RTP_BADVALUEPROPERTY);
475 if (obj provides pr) { (obj.pr)-->(V+COL_HSIZE) = val; }
476 }
477];
Printing Property Names.
Inform doesn't print property names prettily; it more or less prints them only as decimal numbers.
484[ PROPERTY_TY_Say v;
485 print "property ", v;
486];