Against my better judgement, I’m messing around with backdrops again, and writing code that refers to arbitrary backdrops. (If I’ve asked this before, apologies. Couldn’t find if I asked anywhere.)
What I need is a way of determining the list of rooms that a given backdrop is presently in. I could create a new relation between rooms and backdrops, but that would be a royal pain (and seems super-inefficient). I’m not using regions, and defining them would be as onerous as creating a new relation.
(I know that backdrops, strictly speaking, have no location. But they surely have some kind of relation to the places where they appear. I want that.)
I suspect doing this properly would require dipping into I6 (which is beyond my skills); there seems to be a “found_in” I6 property which contains this information, but AFAICT it’s not exposed by the Standard Library.
However, as a quick and really ugly hack, the following brute force approach seems to more or less work:
To decide whether (the item - a backdrop) is found in (the spot - a room):
say run paragraph on; [avoid introducing a line break if this rule is called in the middle of printing text]
let the former location be the location;
move the player to the spot, without printing a room description;
let the result be whether or not the item is in the location;
move the player to the former location, without printing a room description;
decide on the result.
That was… one of the uglier last-resort options, yeah. I’m having flashbacks to the teleporting penis in Bob’s Garage.
The alternative is to just re-invent backdrops as magical teleporting things that pre-emptively follow the player around as appropriate, which would be pretty straightforward.
Alas, that doesn’t seem to work as you suggest; in fact, Inform seems to interpret “now X is in Y” as an alias for “move X to Y”, which will print the room description if X is the player.
Ps. Here’s my test story, if anyone wants to play with it:
"Something Fishy" by Vyznev Xnebara
The Entrance is a room.
The Living Room is south of the Entrance.
The Office is east of the Living Room.
The Kitchen is west of the Living Room.
The unpleasant smell is a backdrop. "An unpleasant smell lingers here." It is not scenery.
The unpleasant smell is in the Kitchen.
Definition: a room is smelly if the smell is found in it.
Rule for printing the locale description when the unpleasant smell is not in the location:
say "You smell something funny coming from [the list of smelly rooms]."
To decide whether (the item - a backdrop) is found in (the spot - a room):
say run paragraph on;
let the former location be the location;
move the player to the spot, without printing a room description;
let the result be whether or not the item is in the location;
move the player to the former location, without printing a room description;
decide on the result.
(a) a function that works exactly like this but in I6, so it’s more efficient;
or
(b) define a separate region-in property for backdrops, and make sure it’s always the same as I7’s internal notion of where-the-damn-backdrop is. (This requires you to be careful every time you define or move a backdrop. And if you move a backdrop to a description of rooms, rather than a region, you’d need a second property, I suppose.)
That’s pretty much exactly what backdrops are; internally, they are also referred to as “floating objects” since they are silently moved into the player’s location as she moves.
Surely the I6 code to populate a list with the contents of an object’s found_in array isn’t that difficult? The only complication would seem to be manipulating the list in I6, but there are helper functions for that. I don’t have time right now to give it a go, but maybe someone who’s quicker with I6 than I could try rolling it out?
Went with Vyznev’s solution as the least objectionable of a bad lot. I7 emits slight wheeze and a billow of steam, but no alarming clunking noises as yet. Will monitor closely.
I see. It still seems as though one ought to be able to test that function w/o moving the player. That should be a lot faster: just one function call per location in the game rather than going through all of the processes of moving the player through all the rooms. Unfortunately, I tried a couple of different ways to implement this, and didn’t succeed:
Based solely on zarf’s description (“found_in is a function that tests the global location variable”), I tried caching the location variable, changing the location to an arbitrary room, calling found_in for the backdrop, e.g., sun.found_in or sun.found_in(), and then restoring the location. Unfortunately, this seems to always return true.
I tried to adapt some of the library code (from MoveFloatingObjects) to this purpose and found that my implementation always returns false…
Here’s what I came up with for #2 in case anyone is interested:
[spoiler][code]House is a room. Garden is a room. Field is a room.
Sun is a backdrop. Sun is in Field and Garden.
To decide whether (B - a thing) is a floating object in (R - a room):
(- TestFloatingObjects({B},{R}) -)
Include (-
[ TestFloatingObjects o r k l m address flag;
address = o.&found_in;
if (address ~= 0 && o hasnt absent) {
if (ZRegion(address–>0) == 2) {
m = address–>0;
.TestPropositionally;
if (m.call(r) ~= 0) return true;
}
else {
k = o.#found_in;
for (l=0 : l<k/WORDSIZE : l++) {
m = address–>l;
if (ZRegion(m) == 2) jump TestPropositionally;
}
}
}
return false;
];
-)
When play begins:
if the Sun is a floating object in the Field, say “It’s in the field!”;
if the Sun is a floating object in the House, say “It’s in the house!”;[/code][/spoiler]
I could also make Vyznev’s solution more efficient by using the jump-the-player technique only once for each backdrop, and using that to populate a list of rooms for each backdrop, which would be referred to thereafter.
(Of course, I could just write all the lists out in advance: but that would introduce lots of opportunity for transcription errors.)
So I did, and so it does! Thanks for taking a look!
Here’s the full implementation/example for maga’s request:
[code]House is a room. Garden is a room. Field is a room.
Sun is a backdrop. Sun is in Field and Garden.
To decide whether (B - a thing) is a floating object in (R - a room):
(- TestFloatingObjects({B},{R}) -)
Include (-
[ TestFloatingObjects o r k l m address flag;
address = o.&found_in;
if (address ~= 0 && o hasnt absent) {
if (ZRegion(address–>0) == 2) {
m = address–>0;
.TestPropositionally;
if (m.call(r) ~= 0) return true;
}
else {
k = o.#found_in;
for (l=0 : l<k/WORDSIZE : l++) {
m = address–>l;
if (ZRegion(m) == 2) jump TestPropositionally;
if (m == r || m in r) rtrue;
}
}
}
return false;
];
-)
To decide what list of rooms is the list of loci for (B - a backdrop):
let L be a list of rooms;
repeat with locus running through rooms:
if B is a floating object in locus, add locus to L;
decide on L.
When play begins:
say “[List of loci for the Sun].”;[/code]
I’m afraid this does not work when the backdrop is set to a region. That’s the case when found_in tests location rather than its argument. Yes, it’s inconsistent.
You need to do something like this:
To decide whether (B - a thing) is a floating object in (R - a room):
(- WrapTestFloatingObjects({B},{R}) -)
[ WrapTestFloatingObjects b r res tmploc;
tmploc = location;
location = r;
res = TestFloatingObjects(b, r);
location = tmploc;
return res;
];
I came across this problem when updating Conditional Backdrops. I think there might have been an Inform bug involving the found_in property - like it’s supposed to take an argument, but it ignores the argument and uses the location instead. I think I fixed that, with Zarf’s guidance… which makes the problem a simple matter of writing an I7 wrapper for found_in.
Hmm, now I’m encountering a weird bug with this. It works fine when backdrops are in only a few locations; but for backdrops that are in a lot of locations (I think at least 16), it adds every location to the loci list.