Listing parts of a thing like it were contents of a container

I have a spacesuit in my story, and I’m building a framework to add modular components to it. Things like jet boosters, comms, etc. This clearly sounds like a use case for the incorporation relation, but I’m struggling with how to represent this to the player, particularly when they’re taking inventory. Is there some activity I can hook into that lists the parts of a thing as if it were a container containing those parts? I suppose I could just model the spacesuit as a container, but it feels more intuitive to use incorporation.

This code example sort of works, though I don’t like how it’s all in the inventory details. You can imagine it’ll start to get unwieldy after too many installed parts, which is why I stop listing them once you get to 4.

The spacesuit is a thing. The description is "It's not exactly sleek, but it avoids the fishbowl-and-wrinkled-sock look from humanity's earliest forays into space. It is currently installed with [a list of installed things]." The player wears the spacesuit. Understand "suit" as the spacesuit.

A gadget is a kind of device. A gadget is usually switched off. A gadget can be installed. Definition: a gadget is installed if it is part of the spacesuit.

Rule for printing inventory details of the spacesuit:
	if the number of installed things is 0:
		stop;
	otherwise if 4 things are installed:
		say " ([number of installed things] non-standard components)[run paragraph on]";
	otherwise:
		say " ([number of installed things] non-standard component[s]: [list of installed things])[run paragraph on]".

Any way that I can have it show an indented list beneath the spacesuit, like the contents of a container, without actually making it a container?

Take a look at Recipe Book 6.7, especially the example “Equipment List”.

There’s code in one that demonstrates this as
list the contents of the player, with newlines, indented, giving inventory information, including contents, with extra indentation.

I believe you can use these types of specifications in rules that aren’t necessarily for taking inventory.

That is the obvious choice, but unfortunately it only works for contents, not parts…

Could you do something like (untested, but it seems something like this should work):

Rule for printing the name of the spacesuit while taking inventory:
     say "Spacesuit:[line break][a list of things incorporated by the spacesuit, with newlines, indented][line break]"

You might not need “a” as in [a list of things] if you don’t want the articles, you may want [list of things incorporated...]

Do you mean something like this?

"Listing Components IN SPACE"

Space is a room.

The spacesuit is a thing. The description is "It's not exactly sleek, but it avoids the fishbowl-and-wrinkled-sock look from humanity's earliest forays into space. It is currently installed with [a list of installed things]." The player wears the spacesuit. Understand "suit" as the spacesuit.

A gadget is a kind of device. A gadget is usually switched off. A gadget can be installed. Definition: a gadget is installed if it is part of the spacesuit.

Rule for printing inventory details of the spacesuit:
    if the number of installed things is 0:
	    stop;
    otherwise if at most 3 things are installed:
	    say " ([number of installed things] non-standard component[s]: [list of installed things])[run paragraph on]";
    otherwise:
	    say " ([number of installed things] non-standard components)[line break][run paragraph on]";
	    list installed gadgets with indent 8.

To say (N - a number) spaces: 
    repeat with index running from 1 to N: 
	    say " ".

To list (D - description of values of kind K) with indent (N - number):
    let L be D;
    repeat with current item running through L:
	    say N spaces;
	    say "[current item][line break]".

A gadget called a jet pack is part of the spacesuit.

A gadget called a comm unit is part of the spacesuit.

A gadget called a galactic compass is part of the spacesuit.

A gadget called a pez dispenser is part of the spacesuit.

Test me with "i".

The list-writer in I7 (or rather, the syntax for it) is surprisingly limited. You only get the full set of style options when using list the contents of ... (which only works for containers and supporters). For arbitrary lists, you are limited to a few predefined styles (none of which use the “newlines” option).

You can either write your own definition, which might look something like this:

To list the spacesuit-parts:
	(-
		objectloop({-my:1} ofclass (+ gadget +))
			if ((CoreOf({-my:1}) == (+ the spacesuit +)) && ({-my:1} ~= (+ the spacesuit +)))
				give {-my:1} workflag2;
			else
				give {-my:1} ~workflag2;
		WriteListOfMarkedObjects(NEWLINE_BIT+INDENT_BIT+RECURSE_BIT+EXTRAINDENT_BIT);
	-).

Or, perhaps preferably, write the listing logic in I7, as shown by @otistdog .

1 Like

This’ll do it. The programmer in me doesn’t love that the number indents is hardcoded, but I think it’s unlikely the spacesuit will ever be in a nested list of containment or anything else that would mess with that. And if that ever does seem possible to happen, I can just calculate how many levels of indentation there would be.

Though I did have to change it to indent 4 to match how the containment listing looks. I assume the size of an indent is variable between different interpreters, but that seems like a minor issue - this code works well.

Thanks!

1 Like

I6 code always baffles me, I should learn it one of these days. But besides that, I don’t think the indentation bits work here. When I tested the code, it was flush with the left side of the listing.

No, getting the indentation to work properly would require redefining WriteListOfMarkedObjects, adding a whole slew of cryptic code:

I6 version with working indentation
Include (-
[ WLOMOWithIndentation style indent
	obj common_parent first mixed_parentage length g gc;

	gc = -2;
	objectloop (obj ofclass Object && obj has workflag2) {
		length++;
		if (first == nothing) { first = obj; common_parent = parent(obj); }
		else { if (parent(obj) ~= common_parent) mixed_parentage = true; }
		g = GetGNAOfObject(obj); g = g%3;
		if (gc == -2) gc = g;
		else if (gc ~= g) gc = -1;
	}
	if (mixed_parentage) common_parent = nothing;

	if (length == 0) {
		if (style & ISARE_BIT ~= 0) LIST_WRITER_INTERNAL_RM('W');
		else if (style & CFIRSTART_BIT ~= 0) LIST_WRITER_INTERNAL_RM('X');
		else LIST_WRITER_INTERNAL_RM('Y');
	} else {
		@push MarkedObjectArray; @push MarkedObjectLength;
		MarkedObjectArray = RequisitionStack(length);
		MarkedObjectLength = length;
		if (MarkedObjectArray == 0) return RunTimeProblem(RTP_LISTWRITERMEMORY); 

		if (common_parent) {
			ObjectTreeCoalesce(child(common_parent));
			length = 0;
			objectloop (obj in common_parent) ! object tree order
				if (obj has workflag2) MarkedObjectArray-->length++ = obj;
		} else {
			length = 0;
			objectloop (obj ofclass Object) ! object number order
				if (obj has workflag2) MarkedObjectArray-->length++ = obj;
		}

		WriteListFrom(first, style, indent, false, MarkedListIterator);

		FreeStack(MarkedObjectArray);
		@pull MarkedObjectLength; @pull MarkedObjectArray;
	}
	prior_named_list = length;
	prior_named_list_gender = gc;
	return;
];
-).

To list the spacesuit-parts:
	(-
		objectloop({-my:1} ofclass (+ gadget +))
			if ((CoreOf({-my:1}) == (+ the spacesuit +)) && ({-my:1} ~= (+ the spacesuit +)))
				give {-my:1} workflag2;
			else
				give {-my:1} ~workflag2;
		WLOMOWithIndentation(NEWLINE_BIT+INDENT_BIT+RECURSE_BIT+EXTRAINDENT_BIT, 1);
	-).

Nice!

It didn’t please me, either. This is somewhat more functional than the previous version – indenting is automatic and adjusted as needed in normal inventory listing. (See revised test me.)

To get this, I had to make a small modification to a routine called WriteAfterEntry(), to get it to record the current list writer recursion depth in a global variable called c_depth. (This variable is already included in 6M62-generated code, but I did not see any functional use of it anywhere, so hopefully this change is safe.)

Better Example
"Listing Components IN SPACE"

Space is a room.

The spacesuit is a wearable thing. The description is "It's not exactly sleek, but it avoids the fishbowl-and-wrinkled-sock look from humanity's earliest forays into space. It is currently installed with [a list of installed things]." [The player wears the spacesuit.] Understand "suit" as the spacesuit.

A gadget is a kind of device. A gadget is usually switched off. A gadget can be installed. Definition: a gadget is installed if it is part of the spacesuit.

The player carries a bucket. A basket is in the bucket. The spacesuit is in the basket.

Rule for printing inventory details of the spacesuit:
    if the number of installed things is 0:
	    stop;
    otherwise if at most 3 things are installed:
	    say " ([number of installed things] non-standard component[s]: [list of installed things])[run paragraph on]";
    make no decision.

After printing inventory details of the spacesuit:
    if the number of installed things is at least 4:
	    say " on which are currently installed [number of installed things] non-standard components:";
	    list installed gadgets with indent current indentation depth.

After printing room description details of the spacesuit:
    say " (with [number of installed things] non-standard component[s]: [list of installed things])";

To say (N - a number) spaces: 
    repeat with index running from 1 to N: 
	    say " ".

Current indentation depth is a number that varies. The current indentation depth variable translates into I6 as "c_depth".

To list (D - description of values of kind K) with indent (N - number):
    let L be D;
    repeat with current item running through L:
	    say "[line break]";
	    say (N plus 2) times two spaces;
	    say "[a current item]".

A gadget called a jet pack is part of the spacesuit.

A gadget called a comm unit is part of the spacesuit.

A gadget called a galactic compass is part of the spacesuit.

A gadget called a pez dispenser is part of the spacesuit.

Test me with "i / put spacesuit in bucket / i / wear suit / i / remove it / drop it / look".



Include
(-

! ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
! ListWriter.i6t: Write After Entry (modified)
! ==== ==== ==== ==== ==== ==== ==== ==== ==== ====

[ WriteAfterEntry o depth
    p recurse_flag parenth_flag eldest_child child_count combo;

    c_depth = depth; ! MODIFIED (c_depth does not appear to be in use?)

    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))
			    && (child(o)==0))                    combo=combo+4;
		    if (combo) LIST_WRITER_INTERNAL_RM('A'); ! space and open bracket
		    switch (combo) {
			    1: LIST_WRITER_INTERNAL_RM('D', o);
			    2: LIST_WRITER_INTERNAL_RM('E', o);
			    3: LIST_WRITER_INTERNAL_RM('H', o);
			    4: LIST_WRITER_INTERNAL_RM('F', o);
			    5: LIST_WRITER_INTERNAL_RM('I', o);
			    6: LIST_WRITER_INTERNAL_RM('G', o);
			    7: LIST_WRITER_INTERNAL_RM('J', o);
		    }
		    if (combo) LIST_WRITER_INTERNAL_RM('B'); ! close bracket
	    }
        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);  parenth_flag = true; }
		    else {
			    if (o has light)           { LIST_WRITER_INTERNAL_RM('A'); LIST_WRITER_INTERNAL_RM('D', o);  parenth_flag = true; }
			    if (o has worn)            { LIST_WRITER_INTERNAL_RM('A'); LIST_WRITER_INTERNAL_RM('L', o); parenth_flag = true; }
		    }
    
		    if (o has container)
			    if (o has openable) {
				    if (parenth_flag) {
					    #Ifdef SERIAL_COMMA; print ","; #Endif;
					    LIST_WRITER_INTERNAL_RM('C');
				    } else            LIST_WRITER_INTERNAL_RM('A', o);
				    if (o has open)
					    if (child(o)) LIST_WRITER_INTERNAL_RM('M', o);
					    else          LIST_WRITER_INTERNAL_RM('N', o);
				    else
					    if (o has lockable && o has locked) LIST_WRITER_INTERNAL_RM('P', o);
					    else                                LIST_WRITER_INTERNAL_RM('O', o);
				    parenth_flag = true;
			    }
			    else
				    if (child(o)==0 && o has transparent)
					    if (parenth_flag) { LIST_WRITER_INTERNAL_RM('C'); LIST_WRITER_INTERNAL_RM('F'); }
					    else              { LIST_WRITER_INTERNAL_RM('A'); LIST_WRITER_INTERNAL_RM('F'); 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); 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);
                } else LIST_WRITER_INTERNAL_RM('S', o);
            }
            recurse_flag = true;
        }
        if (o has container && (o has open || o has transparent)) {
            if (c_style & ENGLISH_BIT) {
                if (c_style & TERSE_BIT) {
                	LIST_WRITER_INTERNAL_RM('A', o);
                	LIST_WRITER_INTERNAL_RM('T', o);
                } else LIST_WRITER_INTERNAL_RM('U', o);
            }
            recurse_flag = true;
        }
    }

    if (recurse_flag && (c_style & ENGLISH_BIT)) {
    	SetLWI(child_count, -1, eldest_child);
    	LIST_WRITER_INTERNAL_RM('V', o); 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');
    }
];

-) instead of "Write After Entry" in "ListWriter.i6t".

This works fantastically. The more I6 code I’m exposed to the more likely I might learn it myself one day, so I’m happy this turned out to be such a prime use case for it!