Translucent enterable openable container?

I quickly noticed while doing some simple prototyping, that something like a tent, which I defined as an enterable openable container, is dark when entered and closed, even if the room it’s in is lit.

I’d like to define a translucent container such that when inside it and closed, visibility matches what it’s in (an outer container, or the room). Similarly, if lit (whether open or closed) and looked at from directly outside where there is no light source, it is visible and appears glowing (and provides sufficient light to that outer location).

Similarly, it would be nice to have a translucent door that allows light to show through from the a lit room into a dark room.

From what I’ve read, visibility is tricky, so I don’t know how to implement such a container, but for someone in the know, it should be easy?

The word for this in Inform is “transparent”; the opposite is “opaque”.

By default this won’t let light shine through doors, but it will let it shine through containers.

Hmmm… but I don’t want looking to be able to see into or out of the container.
So I guess I could define a translucent container as a transparent one that blocks looking into or out of, but otherwise lets the light rules work as transparent?

Ah, yes, that’s the trick. You can use a “deciding the concealed possessions” rule to prevent seeing objects through it, but this leads to some undesirable messages (like “in the tent you can see nothing” when examining it from the outside, since it’s assumed you can see in, just can’t see the things in it).

The easiest way would probably be to define a new property “translucent”, as you say, and then alter all the light calculation code to check it instead of “transparent”. A bit tedious but not too awful.

1 Like

Sounds like a worthy extension for me to work on :white_check_mark:

The default behavior is that lit items can be concealed and behave like normal lit items, so you can go ahead and make something transparent and containing a lit thing and it will light up a dark room. Like Daniel says, you’ll want to customize the output for searching and examining.

Beware letting one be portable, though (supporters are not portable by default, but containers are). Through 10.1, taking inventory will reveal to a player everything they enclose, ignoring concealment. In the forthcoming release (schedule unknown), while nothing a player wears or directly carries can be concealed from them, containers or supporters the player encloses that conceal things won’t have the surprise ruined by taking inventory anymore.

This can be done with relatively minor surgery at the I6 level in 6M62. There are some knock-on effects that need to be dealt with, so the list of things to touch include:

  • OffersLight()
  • WriteAfterEntry()
  • the standard notable locale objects rule
  • rules for the examining action
  • rules for the searching action

Here’s sample code for v10.1.2 following the lines suggested by Draconis:

Translucent Tent
"Translucent Tent"

Section - Translucency

A container can be translucent.

The translucent property translates into I6 as "translucent".

Section - Changing visibility for translucency

Include (-

[ OffersLight obj j;
	while (obj) {
		if (obj has light) rtrue;
		objectloop (j in obj) if (HasLightSource(j)) rtrue;
		if ((obj has container) && (obj hasnt open) && (~~(GProperty(OBJECT_TY, obj, translucent))) && (obj hasnt (+ translucent +))) rfalse;
		obj = HolderOf(obj);
	}
	rfalse;
];

-) replacing "OffersLight".

Section - Correcting WriteAfterEntry

Include (-

[ WriteAfterEntry o depth
	p recurse_flag parenth_flag eldest_child child_count combo;

	inventory_stage = 2;
	if (c_style & PARTINV_BIT) {
		BeginActivity(PRINTING_ROOM_DESC_DETAILS_ACT, o);
		if (ForActivity(PRINTING_ROOM_DESC_DETAILS_ACT, o) == false) {
			combo = 0;
			if (o has light && location hasnt light)
				combo=combo+1;
			if (o has container && o hasnt open)
				combo=combo+2;
			if ((o has container && (o has open || o has transparent)) && (UnconcealedChildren(o)==false))                    
				combo=combo+4;
			if (combo) {
				LIST_WRITER_INTERNAL_RM('A');	! "("
				!print (name) o, " has container? ", (o has container), "^";
				!print (name) o, " has open? ", (o has open), "^";
				!print (name) o, " has transparent? ", (o has transparent), "^";
				!print (name) o, " has open or transparent? ", (o has open || o has transparent), "^";
				!print "<child(", (name) o, ")? ", (name) child(o), ">";
				!print "<child(", (name) o, ")==0? ", (child(o) == 0), ">";
				!print "<UnconcealedChildren(", (name) o, ")? ", UnconcealedChildren(o), ">";
				!print "<UnconcealedChildren(", (name) o, ") == false? ", (UnconcealedChildren(o)==false), ">";
				switch (combo) {
					1: LIST_WRITER_INTERNAL_RM('D', o);	! "providing light" 
					2: LIST_WRITER_INTERNAL_RM('E', o);	! "closed"
					3: LIST_WRITER_INTERNAL_RM('H', o);	! "closed and providing light"
					4: LIST_WRITER_INTERNAL_RM('F', o);	! "empty"
					5: LIST_WRITER_INTERNAL_RM('I', o);	! "empty and providing light"
					6: LIST_WRITER_INTERNAL_RM('G', o);	! "closed and empty"
					7: LIST_WRITER_INTERNAL_RM('J', o);	! "closed, empty, and providing light"
				}
				LIST_WRITER_INTERNAL_RM('B');	! ")"
			}
		}
		EndActivity(PRINTING_ROOM_DESC_DETAILS_ACT, o);
	}   ! end of PARTINV_BIT processing

	if (c_style & FULLINV_BIT) {
		BeginActivity(PRINTING_INVENTORY_DETAILS_ACT, o);
		if (ForActivity(PRINTING_INVENTORY_DETAILS_ACT, o) == false) {
			if (o has light && o has worn) {
				LIST_WRITER_INTERNAL_RM('A');	! "("
				LIST_WRITER_INTERNAL_RM('K', o);	! "providing light and being worn"
				parenth_flag = true;
			} else {
				if (o has light) {
					LIST_WRITER_INTERNAL_RM('A');	! "("
					LIST_WRITER_INTERNAL_RM('D', o);	! "providing light"
					parenth_flag = true;
				}
				if (o has worn) {
					LIST_WRITER_INTERNAL_RM('A');	! "("
					LIST_WRITER_INTERNAL_RM('L', o);	! "being worn" 
					parenth_flag = true;
				}
			}
	
			if (o has container) {
				if (o has openable) {
					if (parenth_flag) {
						#Ifdef SERIAL_COMMA; print ","; #Endif;
						LIST_WRITER_INTERNAL_RM('C');	! " and "
					} else
						LIST_WRITER_INTERNAL_RM('A', o);	! "("
					if (o has open) {
						if (UnconcealedChildren(o))
							LIST_WRITER_INTERNAL_RM('M', o);	! "open"
						else
							LIST_WRITER_INTERNAL_RM('N', o);	! "open but empty"
					} else {
						if (o has lockable && o has locked)
							LIST_WRITER_INTERNAL_RM('P', o);	! "closed and locked"
						else
							LIST_WRITER_INTERNAL_RM('O', o);	! "closed"
					}
					parenth_flag = true;
				} else if (UnconcealedChildren(o)==false && o has transparent) {
					if (parenth_flag) {
						LIST_WRITER_INTERNAL_RM('C');	! " and "
						LIST_WRITER_INTERNAL_RM('F');	! "empty"
					} else {
						LIST_WRITER_INTERNAL_RM('A');	! "("
						LIST_WRITER_INTERNAL_RM('F');	! "empty"
						LIST_WRITER_INTERNAL_RM('B');	! ")"
					}
				}
			}
			if (parenth_flag) LIST_WRITER_INTERNAL_RM('B');	! ")"
		}
		EndActivity(PRINTING_INVENTORY_DETAILS_ACT, o);
	}   ! end of FULLINV_BIT processing

	child_count = 0;
	eldest_child = nothing;
	objectloop (p in o) {
		if ((c_style & CONCEAL_BIT == 0) || (ConcealedFromLists(p) == false)) {
			if (p has list_filter_permits) {
				child_count++;
				if (eldest_child == nothing)
					eldest_child = p;
			}
		}
	}

	if (child_count && (c_style & ALWAYS_BIT)) {
		if (c_style & ENGLISH_BIT) {
			print " ";
			LIST_WRITER_INTERNAL_RM('Q', o);	! "containing"
			print " ";
		}
		recurse_flag = true;
	}
	
	if (child_count && (c_style & RECURSE_BIT)) {
		if (o has supporter) {
			if (c_style & ENGLISH_BIT) {
				if (c_style & TERSE_BIT) {
					LIST_WRITER_INTERNAL_RM('A', o);	! "("
					LIST_WRITER_INTERNAL_RM('R', o);	! "on whom/which"
				} else
					LIST_WRITER_INTERNAL_RM('S', o);	! ", on top of whom/which"
			}
			recurse_flag = true;
		}
		if (o has container && (o has open || o has transparent) && (UnconcealedChildren(o) == true)) {
			if (c_style & ENGLISH_BIT) {
				if (c_style & TERSE_BIT) {
					LIST_WRITER_INTERNAL_RM('A', o);	! "("
					LIST_WRITER_INTERNAL_RM('T', o);	! "in whom/which"
				} else
					LIST_WRITER_INTERNAL_RM('U', o);	! ",inside whom/which"
			}
			recurse_flag = true;
		}
	}

	if (recurse_flag && (c_style & ENGLISH_BIT)) {
		SetLWI(child_count, -1, eldest_child);
		LIST_WRITER_INTERNAL_RM('V', o);	! "is/are"
		print " ";
	}

	if (c_style & NEWLINE_BIT) new_line;

	if (recurse_flag) {
		o = child(o);
		@push lt_value; @push listing_together; @push listing_size;
		@push c_iterator;
		c_iterator = ObjectTreeIterator;
		lt_value = EMPTY_TEXT_VALUE; listing_together = 0; listing_size = 0;
		WriteListR(o, depth+1, true);
		@pull c_iterator;
		@pull listing_size; @pull listing_together; @pull lt_value;
		if (c_style & TERSE_BIT)
			LIST_WRITER_INTERNAL_RM('B');	! ")"
	}
];

-) replacing "WriteAfterEntry".

Include (-

[ UnconcealedChildren obj ch_obj flag;
	for ( ch_obj = child(obj) : ch_obj : ch_obj = sibling(ch_obj))
		if (TestConcealment(obj, ch_obj) == false) rtrue;
	rfalse;
];

-).

Section - Preventing gratuitous post-looking observation of not-really-nothing

For choosing notable locale objects (this is the fixed standard notable locale objects rule):
	let the domain be the parameter-object;
	let the held item be the first thing held by the domain;
	while the held item is a thing:
		if the player can see held item:
			set the locale priority of the held item to 5;
		now the held item is the next thing held after the held item;
	continue the activity.

The fixed standard notable locale objects rule is listed instead of the standard notable locale objects rule in the for choosing notable locale objects rules.

Section - Preventing gratuitous mid-examining observation of not-really-nothing

Carry out examining (this is the fixed examine containers rule):
	if the noun is a container:
		if the noun is open or the noun is transparent:
			if something visible described which is not scenery is in the noun and something which
				is not the player is in the noun:
				say "In [the noun] " (A);
				list the contents of the noun, as a sentence, tersely, not listing
					concealed items, prefacing with is/are;
				say ".";
				now examine text printed is true;
			otherwise if examine text printed is false:
				if the player is in the noun:
					make no decision;
				say "[The noun] [are] empty." (B);
				now examine text printed is true;

The fixed examine containers rule is listed instead of the examine containers rule in the carry out examining rules.

Section - Preventing gratuitous post-searching observation of not-really-nothing

Report searching a container (this is the fixed standard search containers rule):
	if the noun contains a visible described thing which is not scenery:
		say "In [the noun] " (A);
		list the contents of the noun, as a sentence, tersely, not listing
			concealed items, prefacing with is/are;
		say ".";
	otherwise:
		say "[The noun] [are] empty." (B).

The fixed standard search containers rule is listed instead of the standard search containers rule in the report searching rules.

Section - Scenario

[Note that this scenario is based on an example posted by mathbrush on another thread; hopefully he won't mind.]
Pasture is a room.

A cat, a dog, a goat, and a cow are in Pasture.

The box is an open container in Pasture. The box is fixed in place.

The tent is a closed openable enterable translucent container. It is in Pasture.

Rule for deciding the concealed possessions of the tent:
	if the particular possession is the cat:
		decide yes;
	decide no.

Test me with "x tent / open tent / x tent / look / put cow in tent / look / x tent / put cat in tent / look / x tent / get cow / look / x tent / purloin cat / enter tent / look / close tent / look / drop cow / look / drop cat / look / get cow / look / open tent / look".

(EDIT: Fixed issues in preceding code that were identified by BadParser below.)

There was a property called inside_description in I6 that was useful for this sort of situation, and it hasn’t been completely eradicated from the template code (in 6M62, hopefully still the case in 10.1.2). To handle situations where the player is inside the tent, it may be desirable to add:

Inside description
Section - Inside description

A thing has some text called inside description. The inside description property translates into I6 as "inside_description". [Use the adjective "empty" to refer to objects with no defined inside description]

Carry out looking (this is the inside description body text rule):
	if the visibility level count is 0:
		if set to abbreviated room descriptions, continue the action;
		if set to sometimes abbreviated	room descriptions and
			abbreviated form allowed is true and
			darkness witnessed is true,
			continue the action;
	otherwise if the visibility ceiling is a container or the visibility ceiling is a supporter:
		if set to abbreviated room descriptions, continue the action;
		if set to sometimes abbreviated	room descriptions and abbreviated form
			allowed is true and the location is visited, continue the action;
		if the inside description of the visibility ceiling is not empty:
			say the inside description of the visibility ceiling;
			say paragraph break;

The inside description body text rule is listed before the room description paragraphs about objects rule in the carry out looking rules.

(EDIT 2: Fixed “double darkness” problem for inside description as identified by BadParser below and added more rule adjustments to example scenario.)

so that it’s possible to set up a room-like description:

The inside description of the tent is "A bit musty, but perfectly serviceable."
1 Like

I can confirm this technique still works with 10.1.2.

However, the “Translucent Tent” example has a bug which I haven’t tracked down yet. Partial transcript:

>[7] x tent
In the tent is a cow.

>[8] put cat in tent
(first taking the cat)
You put the cat into the tent.

>[9] look
Pasture
You can see a dog, a goat, a box (empty) and a tent (empty) (in which is a cow) here.

The extra (and incorrect) “(empty)” goes away without the new WriteAfterEntry replacement.

I’m getting a double printing of “[It] [are] pitch dark, and [we] [can’t see] a thing.” message with this. Shouldn’t it be:

The inside description body text rule is listed instead of the room description body text rule in the carry out looking rules.

Thank you, BadParser! The UnconcealedObjects() routine had some serious problems. I’ve edited the example scenario.

For the “double darkness” problem, I think it should really be a “this rule substitutes for that when…” construction, but I understand those aren’t working correctly at the moment for 10.1.

EDIT: I clipped that part of the rule for inside descriptions, and I also added some rules adjustments for the examining and searching actions for consistent treatment of concealed things. I guess those are really a separate problem, but they’re needed for this scenario to work consistently.

rule substitutes for and all of the concealment flaws I know about are fixed in current dev.