I6 Template Layer

Inform 7 6M62ContentsIntroductionFunction IndexRules Index

RTP.i6t

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; ! DEBUG 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];

Scan Property Metadata.

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); ! to force a run-time problem 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); ! to force a run-time problem 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];