Light contents
Darkness.
"Darkness" is not really a place: but in I6 it has to be an object so that the location-name on the status line can be "Darkness". In I7 we use it as little as possible: note that it has no properties.
13Object thedark "(darkness object)";
Light Measurement.
These two routines, OffersLight and HasLightSource, are largely unchanged from their I6 definitions; see the Inform Designer's Manual, 4th edition, for a commentary. In terms of how they are used in I7, OffersLight is called only by the two rules below; HasLightSource is called also in determining the scope, that is, what is visible to the player.
23[ OffersLight obj j;
24 while (obj) {
25 if (obj has light) rtrue;
26 objectloop (j in obj) if (HasLightSource(j)) rtrue;
27 if ((obj has container) && (obj hasnt open) && (obj hasnt transparent)) rfalse;
28 if ((obj provides component_parent) && (obj.component_parent))
29 obj = obj.component_parent;
30 else
31 obj = parent(obj);
32 }
33 rfalse;
34];
35
36[ HasLightSource i j ad sr po;
37 if (i == 0) rfalse;
38 if (i has light) rtrue;
39 if ((IsSeeThrough(i)) && (~~(HidesLightSource(i))))
40 objectloop (j in i)
41 if (HasLightSource(j)) rtrue;
42 ad = i.&add_to_scope;
43 if (parent(i) ~= 0 && ad ~= 0) {
44 if (metaclass(ad-->0) == Routine) {
45 ats_hls = 0; ats_flag = 1;
46 sr = scope_reason; po = parser_one;
47 scope_reason = LOOPOVERSCOPE_REASON; parser_one = 0;
48 RunRoutines(i, add_to_scope);
49 scope_reason = sr; parser_one = po;
50 ats_flag = 0; if (ats_hls == 1) rtrue;
51 }
52 else {
53 for (j=0 : (WORDSIZE*j)<i.#add_to_scope : j++)
54 if ((ad-->j) && (HasLightSource(ad-->j) == 1)) rtrue;
55 }
56 }
57 if (ComponentHasLight(i)) rtrue;
58 rfalse;
59];
60
61[ ComponentHasLight o obj next_obj;
62 if (o provides component_child) {
63 obj = o.component_child;
64 while (obj) {
65 next_obj = obj.component_sibling;
66 if (obj has light) rtrue;
67 if (HasLightSource(obj)) rtrue;
68 if ((obj provides component_child) && (ComponentHasLight(obj))) rtrue;
69 obj = next_obj;
70 }
71 }
72 rfalse;
73];
74
75[ HidesLightSource obj;
76 if (obj == player) rfalse;
77 if (obj has transparent or supporter) rfalse;
78 if (obj has animate) rfalse;
79 if (obj has container) return (obj hasnt open);
80 return (obj hasnt enterable);
81];
Invariant.
The following routines maintain two variables about the light condition of the player:
(a) If on the most recent check the player was in light, then location equals real_location and lightflag is false. (b) If on the most recent check the player was in darkness, then location equals thedark and lightflag is true.
Note that they are not allowed to alter real_location, whose definition has nothing to do with light.
96Global lightflag = false;
Adjust Light Rule.
This rule fires at least once a turn, and more often when the player moves location, since that's likely to invalidate any previous assumptions. It compares the state of light now with the last time it ran, and gives instructions on what to do in each of the four possibilities.
105[ ADJUST_LIGHT_R previous_light_condition;
106 previous_light_condition = lightflag;
107 lightflag = OffersLight(parent(player));
108
109 if ((previous_light_condition == false) && (lightflag == false)) {
110 location = thedark;
111 rfalse;
112 }
113
114 if ((previous_light_condition == false) && (lightflag == true)) {
115 location = real_location;
116 CarryOutActivity(PRINTING_NEWS_OF_LIGHT_ACT);
117 rfalse;
118 }
119
120 if ((previous_light_condition == true) && (lightflag == false)) {
121 location = thedark;
122 DivideParagraphPoint();
123 BeginActivity(PRINTING_NEWS_OF_DARKNESS_ACT);
124 if (ForActivity(PRINTING_NEWS_OF_DARKNESS_ACT) == false) {
125 ADJUST_LIGHT_RM('A'); new_line;
126 }
127 EndActivity(PRINTING_NEWS_OF_DARKNESS_ACT);
128 rfalse;
129 }
130
131 if ((previous_light_condition == true) && (lightflag == true)) {
132 location = real_location;
133 rfalse;
134 }
135
136 rfalse;
137];
Silent Light Consideration.
The Adjust Light Rule makes a fuss when light changes: it prints messages, for instance. This rule is silent instead, and simply does the minimum necessary to maintain the light invariant. It is used in only four circumstances:
(a) To determine the initial light condition at start of play. (b) When the player moves from one room to another via the "going" action. (c) When the player moves via PlayerTo, which is used by "now the player is in ...". (d) When the player changes from one persona to another.
Perhaps case (b) is surprising. Why not simply use the adjust light rule, as we do on an instance of "move player to ...", for instance? The answer is that the going action is just about to print details of the new location anyway, so it would be redundant to have the adjust light rule print out details as well.
157[ SilentlyConsiderLight;
158 lightflag = OffersLight(parent(player));
159 if (lightflag) location = real_location; else location = thedark;
160 rfalse;
161];
Translucency.
IsSeeThrough is used at various places: roughly speaking, it determines whether obj being in scope means that the object-tree contents of obj are in scope.
169[ IsSeeThrough obj;
170 if ((obj has supporter)
171 || (obj has transparent)
172 || (obj has animate)
173 || ((obj has container) && (obj has open)))
174 rtrue;
175 rfalse;
176];
Visibility Parent.
The idea of VisibilityParent is that it takes us from a given position in the object tree, o, to the next visible position above. Note that (1) A container has an inside and an outside: this routine calculates from the "inside of o", which is why it returns nothing from an opaque closed container; (2) Component parts are (for purposes of this routine) attached to the outside surface of a container, so that if o is part of a closed opaque container then the visibility parent of o is its actual parent.
189[ VisibilityParent o;
190 if (o && (o has container) && (o hasnt open) && (o hasnt transparent)) return nothing;
191 if (o) o = CoreOfParentOfCoreOf(o);
192 return o;
193];
Find Visibility Levels.
The following routine sets the pair of variables visibility_ceiling, the highest visible point in the object tree above the player – or thedark if the player cannot see at all – and visibility_levels, the number of steps of VisibilityParent needed to reach this ceiling; or 0 if the player cannot see at all.
203[ FindVisibilityLevels lc up;
204 if (location == thedark) {
205 visibility_ceiling = thedark;
206 visibility_levels = 0;
207 } else {
208 visibility_ceiling = player;
209 while (true) {
210 up = VisibilityParent(visibility_ceiling);
211 if (up == 0) break;
212 visibility_ceiling = up;
213 lc++;
214 }
215 visibility_levels = lc;
216 }
217];
Touchability Ceiling.
Analogously:
223[ TouchabilityCeiling original o p;
224 o = original;
225 while (o) {
226 p = CoreOfParentOfCoreOf(o);
227 if (p ofclass K1_room) return p;
228 if (p == nothing) return o;
229 if ((FollowRulebook(REACHING_OUTSIDE_RB, p)) && (RulebookFailed()))
230 return p;
231 o = p;
232 }
233 return o;
234];
Scope Ceiling.
Scope is almost the same thing as visibility, but not quite, and the following routine does not quite duplicate the calculation of FindVisibilityLevels. The difference arises in the first step, where we take the parent of pos, not the core of the parent of the core of pos: this makes a difference if pos is inside a container which is itself part of something else.
245[ ScopeCeiling pos c;
246 if (pos == player && location == thedark) return thedark;
247 c = parent(pos);
248 if (c == 0) return pos;
249 while (VisibilityParent(c)) c = VisibilityParent(c);
250 return c;
251];
Object Is Untouchable.
The following routine imitates the I6 library one of the same name, but works instead by delegating the decision to the accessibility rulebook.
It is not easy to answer the question of whether someone other than the player, but in another room, can touch a multiply-present object (a two-sided door or a backdrop) and we err on the side of caution by saying yes in such cases. The question would only be asked in the case of a "try" action deliberately caused by the author, or by an instruction made by the player to someone not in the same room: in the first case, we will assume that the author knows what he is doing, and in the second case, the circumstances in which saying yes would be a wrong call are highly improbable.
268[ ObjectIsUntouchable item silent_flag p save_sp decision moving x;
269 if (LocationOf(p) ~= real_location) {
270 for (x = CoreOf(item): x: x = CoreOfParentOfCoreOf(x)) {
271 if (x ofclass K4_door or K7_backdrop) {
272 moving = true;
273 MoveFloatingObjects(LocationOf(p));
274 break;
275 }
276 }
277 }
278 untouchable_object = item; untouchable_silence = silent_flag;
279 touch_persona = p; if (p == actor) touch_persona = 0;
280 save_sp = say__p; say__p = 0;
281 @push actor; actor = p;
282 if (FollowRulebook(ACCESSIBILITY_RB, 0, true)) {
283 if (RulebookSucceeded()) decision = false;
284 else decision = true;
285 } else decision = false;
286 @pull actor;
287 if (say__p == false) say__p = save_sp;
288 if (moving) MoveFloatingObjects();
289 untouchable_silence = 0;
290 return decision;
291];
Access Through Barriers Rule.
296[ ACCESS_THROUGH_BARRIERS_R ancestor i j external p;
297 p = touch_persona; if (p == 0) p = actor;
298
299 ancestor = CommonAncestor(p, untouchable_object);
300 if ((ancestor == 0) && (LocationOf(untouchable_object) == nothing)
301 && ((untouchable_object ofclass K4_door or K7_backdrop) == false)) {
302 if (touch_persona == 0) {
303 if ((actor == player) && (untouchable_silence == false)) {
304 ACCESS_THROUGH_BARRIERS_RM('A', untouchable_object);
305 new_line;
306 }
307 }
308 RulebookFails();
309 rtrue;
310 }
311
312
313
314 if (CoreOf(p) ~= ancestor) {
315 i = parent(CoreOf(p)); j = CoreOf(i); external = false;
316 if (j ~= i) { i = j; external = true; }
317 while (i~=ancestor && i) {
318 if ((external == false)
319 && (FollowRulebook(REACHING_OUTSIDE_RB, i))
320 && (RulebookFailed())) rtrue;
321 i = parent(CoreOf(i)); external = false;
322 if (~~(i ofclass K5_container)) {
323 j = CoreOf(i);
324 if (j ~= i) { i = j; external = true; }
325 }
326 }
327 }
328
329
330
331 if (CoreOf(untouchable_object) ~= ancestor) {
332
333 i = CoreOf(untouchable_object);
334
335
336 i = parent(i); external = false;
337 while (i~=ancestor && i) {
338 if ((external == false) &&
339 (FollowRulebook(REACHING_INSIDE_RB, i)) &&
340 (RulebookFailed())) rtrue;
341 i = CoreOf(i);
342 if (i == ancestor) break;
343 i = parent(i); external = false;
344 if (~~(i ofclass K5_container)) {
345 j = CoreOf(i);
346 if (j ~= i) { i = j; external = true; }
347 }
348 }
349 }
350
351 RulebookSucceeds();
352 rtrue;
353];
Can't Reach Inside Closed Containers Rule.
358[ CANT_REACH_INSIDE_CLOSED_R;
359 if (parameter_value has container && parameter_value hasnt open) {
360 if (touch_persona == 0) {
361 if ((actor == player) && (untouchable_silence == false)) {
362 CANT_REACH_INSIDE_CLOSED_RM('A', parameter_value);
363 new_line;
364 }
365 }
366 RulebookFails(); rtrue;
367 }
368 rfalse;
369];
Can't Reach Outside Closed Containers Rule.
374[ CANT_REACH_OUTSIDE_CLOSED_R;
375 if (parameter_value has container && parameter_value hasnt open) {
376 if (touch_persona == 0) {
377 if ((actor == player) && (untouchable_silence == false)) {
378 CANT_REACH_OUTSIDE_CLOSED_RM('A', parameter_value);
379 new_line;
380 }
381 }
382 RulebookFails(); rtrue;
383 }
384 rfalse;
385];
Can't Reach Inside Rooms Rule.
390[ CANT_REACH_INSIDE_ROOMS_R;
391 if (parameter_value && parameter_value ofclass K1_room) {
392 if (touch_persona == 0) {
393 if ((actor == player) && (untouchable_silence == false)) {
394 CANT_REACH_INSIDE_ROOMS_RM('A', parameter_value);
395 new_line;
396 }
397 }
398 RulebookFails(); rtrue;
399 }
400 rfalse;
401];
402