New Extension: Actions on Groups

EDIT TWO: My folder on GitHub doesn’t have a dash in its name anymore–fixed URL below. Also, as a reminder, the file attached to this is the older version.

EDIT: Extension now on GitHub; check there for updated versions!

Here’s a new extension, “Actions on Groups,” that’s meant to provide graceful handling of actions that apply to entire groups of things at once. So we can easily implement something like the example “The Left Hand of Autumn,” where we get a special result when examining a group of things that consists of exactly the left glove, the twig, and the pear, saying

rather than getting something like this:

Ordinarily, in order to implement something like this we’d have to write a special rule for dealing with examining when the multiple object list is non-empty, and put in a lot of machinery to make sure that the action doesn’t run once for each object on the multiple object list, and to suppress the announcements like “left glove:”. The Actions on Groups extension handles these cases for us, so that we can produce the desired behavior just by writing:

Examining is groupable action. Understand "examining [things]" as examining. An action on groups rule for examining when dealing with {left glove, pear, twig}: say "One special text dealing with those three particular items."

The extension is attached to this post (and will wind up on Github when I figure out how to Github). It’s inspired by this thread. Enjoy!

EDIT: Fixed the totally messed-up file, or tried to.
Actions on Groups.i7x (21.4 KB)

The extension documentation mentions a tricky implementation of “The Facts Were These.” Here it is:

"The Facts Were These, Refactored"

Include Actions on Groups by Matt Weiner.

Section 1 - Procedure

[From the original example, with the multiple-giving mechanics snipped, and changing the ungivability rules to strip out the flag ("already gave at the office") that the original example used to keep track of whether multiple actions were tried; Actions on Groups takes care of that. The rule for implicitly taking also had to be modified; see below.]

[We start by creating the idea that everything in the game has a monetary value:]

A price is a kind of value. $10 specifies a price. A thing has a price.

A thing can be given or ungiven. A thing is usually ungiven.

[This is for record-keeping purposes so that we can print an attractive list of what was given at the end of the turn.]

[Now we create our own variation of implicitly taking in order to customize the output for the multiply-giving action. The "ungivability rules" should disallow any object that the player absolutely cannot take, because we want "carry out the implicitly taking activity" to succeed every time -- and therefore not print out any less-attractive results from implicit takes that don't succeed. Otherwise, the player's GIVE TREE AND DOG TO ATTENDANT might produce the reply "That's fixed in place" -- without specifying which object is fixed in place.]

[Because of the way this works, we will want to be careful: if we have any "instead of taking..." rules for special objects in the game, we should be sure to mirror those with an ungivability rule to print something more suitable in the case that the player tries taking that object as part of the multiple giving action.]

The ungivability rules are an object-based rulebook.

An ungivability rule for a person:
	say "Slavery is illegal.[paragraph break]" instead.

An ungivability rule for something (called the item) which is enclosed by someone who is not the player:
	say "[The item] [aren't] yours to give.[paragraph break]" instead.

An ungivability rule for something which encloses the player:
	say "You don't want to end up as part of the gift.[paragraph break]" instead;

An ungivability rule for something (called the item) which is part of something:
	say "[The item] [are] attached to [a random thing which incorporates the item][paragraph break]" instead.

An ungivability rule for something (called the item) which is scenery:
	say "[The item] [are] unremovable.[paragraph break]" instead.

An ungivability rule for something (called the item) which is fixed in place:
	say "[The item] [are] fixed in place.[paragraph break]" instead.

An ungivability rule for a direction (called the item):
	say "[The item] [are] not susceptible to giving.[paragraph break]" instead.

Rule for implicitly taking something (called target) while giving: [This is meant to preempt the "(first taking the dollar)" message when we are giving more than one thing. The original example had this as an action on "multiply-giving," but with actions on groups the action is just "giving," so we need to check the multiple object list so that we do not preempt the implicit taking message when we are only giving one thing.]
	if the number of entries in the multiple object list is greater than 0:
		silently try taking the target;
		if the player carries the target:
			add the target to the recently-collected list;
	otherwise:
		continue the activity.

The recently-collected list is a list of objects that varies.

[And now, since this ought to work symmetrically if the player provides just one high-value item:]

Check giving something to someone:
	if the price of the noun is less than the price of the second noun:
		say "[The second noun] angrily rejects your piffling bribe." instead.

[As we've seen elsewhere, the giving action by default returns a refusal, but is also written to start working if we remove the blockage. So we do that here, and revise the report rule to match the report rule we have for multiple giving.]

The block giving rule is not listed in any rulebook.

The new report giving rule is listed instead of the standard report giving rule in the report giving it to rules.

This is the new report giving rule:
	say "[The second noun] rather shamefacedly tucks [the noun] away into a pocket."

[After each instance of the multiply-giving action, we need to clear the variables we used to track its state. We could do this in "Before reading a command", but that's unsafe because the player might type GIVE PIE AND CAP TO ATTENDANT. GIVE DOLLARS TO ATTENDANT. all on a single line, and we would like to be able to clear the variables between one action and the next. The correct place to attach this behavior is immediately before the generate action rule, thus:]

The before-generation rule is listed before the generate action rule in the turn sequence rules.

This is the before-generation rule:
	now every thing is ungiven;
	truncate the recently-collected list to 0 entries.

Section 2 - Implementation of Multiple Giving with Actions on Groups

[Setting up giving as a groupable action:]

Giving is groupable action.

Understand "give [things preferably held] to [someone]" as giving it to.
Understand "give [things] to [someone]" as giving it to.
[as in the original example, we need the [things preferably held] line so that if the player holds two dollars and the attendant holds one, "dollars" will match the dollars the player holds and not the one the attendant holds]

[We now take the check/carry out/report multiple-giving rules from the original example and turn them into Actions on groups for giving rules, in order:]

Last action on groups rule for giving something to the player (this is the can't give multiple things to yourself rule):
		say "You can hardly bribe yourself.[paragraph break]";
		rule fails. [The "instead" should make the rule fail and stop the rest of the actions on groups rules from happening.]

[To quote the original documentation: "The following rule is longish because it processes the entire list at once, generating implicit takes if necessary (but processing those implicit takes silently according to its own special rule, so that the output can be managed attractively). We are also, at the same time, calculating the total value of the player's offer."
Furthermore, for an Actions on Groups rule, if the rule successfully makes it to the end we must "make no decision" so the rulebook will proceed to the next rules.]

Last action on groups rule for giving (this is the check givability and total the bribe-price rule):
	let L be the multiple object list;
	let bribe-price be $0;
	repeat with item running through L:
		if the player does not carry the item:
			anonymously abide by the ungivability rules for the item; 
			[a subtlety here; the original example had "abide by the ungivability rules," and the ungivability rules themselves, if they failed, set the flag that told Inform not to continue trying multiple actions. But for us, that flag gets set when an action on groups rule ends in success or failure. If we merely wrote "abide by," when the ungivability rules ended in failure then the action on groups rule would end with no decision made, while stopping the action on groups rulebook. We need "anonymously abide by," which ensures that if the ungivability rule ends in failure then this rule will also end in failure. See WI §19.14.]
			carry out the implicitly taking activity with the item;
			if the player does not carry the item:
				say "You can't include [the item] in your bribe, since you're not holding [them]![paragraph break]";
				rule fails; [a very subtle subtlety: here and below, we have to write "rule fails" instead of putting "instead" on the previous line, as the in-line "instead" causes the action on groups rulebook to end without result, which will fail to set the flag that makes the group action preempt the ordinary action. "Rule fails" causes both the rule and the rulebook to end in failure.]
		increase bribe-price by the price of item;
	if the number of entries in the recently-collected list is greater than 0:
		repeat with item running through the recently-collected list:
			now item is marked for listing;
		say "You pick up [the list of marked for listing things] and make your offer. [run paragraph on]";
		now everything is unmarked for listing;
	if the bribe-price is less than the price of the second noun:
		say "[The second noun] angrily rejects your piffling bribe.[paragraph break]";
		rule fails; [again, this makes the action on groups rulebook itself fail]
	make no decision. [otherwise the rule will succeed and stop the rest of the actions on groups rules from firing]
	
[From the documentation of the original example: The bit about making some items "marked for listing", above, rather than printing the list directly, is that using the "[the list of....]" syntax guarantees that Inform will respect grouping rules in writing its description. For instance, if the player has automatically taken all three dollars, the output will say "the three dollars" instead of "the dollar, the dollar, and the dollar."]

Last action on groups rule for giving (this is the deliver the loot rule):
	let L be the multiple object list;
	repeat with item running through L:
		now the second noun carries the item;
		now the item is given;
	make no decision. [Again, necessary to allow the rulebook to proceed to the next rule.]
	
Last action on groups rule for giving (this is the report giving as an action on groups rule):
	say "[The second noun] rather shamefacedly tucks [the list of given things] away into a pocket.[paragraph break]". [and since this finishes the rules, we don't write "make no decision," but allow the rule to succeed, so that the giving action won't run for the rest of the objects in the multiple object list.]
	
[By defining each rule as "Last," we guaranteed that they would run in the order they appear in the source code, which is the order we want.]

Section 3 - Scenario

The Morgue Office is a room. "This is not the Morgue itself; this is only its outer office. The familiar room full of silver drawers and cold air lies beyond."

The Morgue Attendant is a man in the Morgue Office. "The Attendant has seen you come through a number of times, and is becoming suspicious of your abiding interest in dead people." The description is "The Morgue Attendant is fifty-four years, six months, five days, and three minutes old." The price of the Morgue Attendant is $3.

A dollar is a kind of thing. The player carries three dollars. The price of a dollar is always $1.

The player carries a miniature rhubarb pie. The price of the miniature rhubarb pie is $5.

The player carries a knitted cap. The price of the knitted cap is $2.

Test me with "test dollars / purloin three dollars / test multi-line / purloin three dollars / purloin pie / purloin cap / test specificity / purloin three dollars / test largesse / test mixed-gift / purloin three dollars / test failure".

Test multi-line with "give dollar and pie to attendant. give dollars and cap to attendant".

Test dollars with "drop all / give dollar to Morgue Attendant / give dollars to Morgue Attendant / get dollars / give dollars to morgue attendant / purloin three dollars / drop dollars / give dollars to Morgue Attendant".

Test specificity with "give three dollars to Morgue Attendant".

Test largesse with "give pie to Morgue Attendant".

Test mixed-gift with "give dollar and cap to Morgue Attendant / get cap / give dollar and cap to morgue attendant / give me and dollar to attendant".

Test failure with "give dollars to Morgue Attendant / give dollars to Morgue Attendant". [This test demonstrates that, when the ungivability rules stop the action, we do not get spurious announcements from the multiple object list.]

This is fairly tricky because the Action on Groups rules must end in either success or failure for the extension to realize that a rule run, and that it should suppress the announcements from the multiple object lists and the ordinary action-processing machinery. The Action on Groups rules for giving themselves call the ungivability rulebook, so we must be sure that if an ungivability rule ends in failure then the Action on Groups rules will also end in failure, meaning that we must “anonymously abide by” the ungivability rules rather than simply abiding by them. Whether this is actually simpler than the original implementation may be a matter of debate.

I tried to open the extension in a notepad program and got a bunch of stuff like this:

I tried different encoding options and it didn’t seem to help.

Well that’s not good. I think it probably has to do with the way I tried to copy it from one place to another in order to get around this issue.

Try this one, and thanks for letting me know!
Actions on Groups.i7x (21.4 KB)

The new one looks good to me!

(and the previous one didn’t look good to me either)

This looks pretty sweet!

(Also, thank you for the mention.)

I’m trying to figure out how to get one special response when multiple glasses are placed on the table. So rather than listing each glass individually, you get a message like “You carefully put three glasses on the table.”

The way I have the rule set up, the glasses never actually end up on the table…and I’d like them to (assuming no other rules prevent them) but I’m not sure of the best way to do that.

The rest of the rule is just trying to deal with the possibility that other things besides glasses could appear in the player’s command.

I haven’t really gotten a handle on the rule success/failure/etc., so those may not be set up correctly.

[code]Include Actions on Groups by Matt Weiner.

A glass is a kind of thing.
The player carries six glasses, a pitcher, and a vase of flowers.

Dining Area is a room.
A table is a supporter in Dining Area.

Test me with “put two glasses on the table / look / put three glasses and the pitcher on the table / look / take all / put four glasses and the vase and the pitcher on the table / look”

Putting something on something is a groupable action.

Action on groups rule for putting glasses on the table:
let G be a number;
let Extras be a list of things;
repeat with item running through the multiple object list:
if item is a glass:
increment G;
otherwise:
add item to Extras;
Say “You carefully set [G in words] glasses on the table.”;
if the number of entries in Extras is 0:
rule succeeds;
if the number of entries in Extras is 1:
let item be entry 1 in Extras;
clear the multiple object list;
try putting item on the table;
rule succeeds;
otherwise if the number of entries in Extras > 1:
alter the multiple object list to Extras;
make no decision.[/code]

Any suggestions?

Include Actions on Groups by Matt Weiner.

A glass is a kind of thing.
The player carries six glasses, a pitcher, and a vase of flowers.

Dining Area is a room.
A table is a supporter in Dining Area.

Test me with "put two glasses on the table / look / put three glasses and the pitcher on the table / look / take all / put four glasses and the vase and the pitcher on the table / look"

Putting something on something is a groupable action.

An action on groups rule for putting a glass on something (called the surface):
	let n be 0;
	let L be a list of things;
	repeat with item running through the multiple object list:
		if the item is a glass:
			increment n;
		otherwise:
			add the item to L;
		now the item is on the surface;
	say "You place [n in words] glass[unless n is 1]es[end unless] on [the surface].";
	if L is not empty, say "You also place [L with definite articles] there."

should work. I think I’d try grouping the “take all” action too, although I don’t offhand know if Matt’s extension might be able to handle that, too.

EDIT: Had a slight false start, but it should be a good starting point now. Although it seems like an unidiomatic way of doing things, to handle all the things with a rule that specifies the glass kind. So I’m also confused, I guess.

This is probably something I should’ve explained better in the documentation, but the Action on Groups rule needs to handle the entire action, including its effect. So the reason the glasses aren’t ending up on the table in this case is that you haven’t told the rule to put them there… the actions will run as usual for everything else, because of the “make no decision,” but by then you’ve removed all the glasses from the multiple object list.

So! All you need to do is put “now the item is on the table” in the code block where you increment G. All the rule succeeds/make no decision stuff looks like you’ve got it working correctly (in fact you don’t even need the “rule succeeds” clauses, I don’t think–as long as you don’t hit the “make no decision” in the final block then the rule will succeed, because the Action on Groups rulebook has default success).

There’s another problem, though, which is much more oh dear for the extension. “Put four glasses and the vase and the pitcher on the table” doesn’t work, I think because when Inform starts with Extras as the multiple object list the noun is still one of the glasses. I tried changing the noun to the first entry of the multiple object list, and that succeeded in getting the vase onto the table, but the announcement for it still said “glass:”–maybe the current item from the multiple object list was already set by that point too? So I tried changing the current item from the multiple object list by hand, and it seems to work, but it makes me feel all hinky.

Here’s what I have now, and the test script is working:

Include Actions on Groups by Matt Weiner.

A glass is a kind of thing.
The player carries six glasses, a pitcher, and a vase of flowers.

Dining Area is a room.
A table is a supporter in Dining Area.

Test me with "put two glasses on the table / look / put three glasses and the pitcher on the table / look / take all / put four glasses and the vase and the pitcher on the table / look"

Putting something on something is a groupable action.

Action on groups rule for putting glasses on the table:
	let G be a number;
	let Extras be a list of things;
	repeat with item running through the multiple object list:
		if item is a glass:
			increment G;
			now the item is on the table;
		otherwise:
			add item to Extras;
	Say "You carefully set [G in words] glasses on the table.";
	if the number of entries in Extras is 0:
		rule succeeds;
	if the number of entries in Extras is 1:
		let item be entry 1 in Extras;
		clear the multiple object list;
		if G > 0:
			say "[item]: [run paragraph on]";
		try putting item on the table;
		rule succeeds;
	otherwise if the number of entries in Extras > 1:
		alter the multiple object list to Extras;
		let item be entry 1 in Extras;
		now the noun is item;
		now the current item from the multiple object list is item;
		make no decision.

(I also wrote something to make "pitcher: " show up before “Done.” in the second test case.)

But another thing is that, as written, the rule doesn’t apply until you hit a glass on the multiple object list–if you write “put vase and three glasses on the table” the result will be screwy. The way to get around that would be to change the rule heading to “Action on groups rule for putting something on the table:”, and make sure if there are no glasses in the list it makes no decision and doesn’t print “You carefully put zero glasses on the table”. Actually since it’ll make no decision as long as there’s anything in the list that isn’t a glass, all you need to do is avoid the “zero glasses” message.

OK, so here’s what I have now:

Include Actions on Groups by Matt Weiner.

A glass is a kind of thing.
The player carries a pitcher, six glasses, and a vase of flowers.

Dining Area is a room.
A table is a supporter in Dining Area.

Test me with "put two glasses on the table / look / put three glasses and the pitcher on the table / look / take all / put four glasses and the vase and the pitcher on the table / look"

Putting something on something is a groupable action.

Action on groups rule for putting something on the table:
	let G be a number;
	let Extras be a list of things;
	repeat with item running through the multiple object list:
		if item is a glass:
			increment G;
			now the item is on the table;
		otherwise:
			add item to Extras;
	if G > 0:
		say "You carefully set [G in words] glasses on the table.";
	if the number of entries in Extras is 0:
		rule succeeds;
	if the number of entries in Extras is 1:
		let item be entry 1 in Extras;
		clear the multiple object list;
		if G > 0:
			say "[item]: [run paragraph on]";
		try putting item on the table;
		rule succeeds;
	otherwise if the number of entries in Extras > 1:
		alter the multiple object list to Extras;
		let item be entry 1 in Extras;
		now the noun is item;
		now the current item from the multiple object list is item;
		make no decision.

Not as intuitive as I hoped! Maybe I should just put a note in the documentation that says that using the extension to consolidate action reports and pass some of the objects through to normal processing is complicated. More likely, it would be a good idea to put in some phrases that automatically handle all the sort of stuff at the end, so you could write something like “try the putting it on action with Extras as a group on the table” and it would invoke a phrase “try (action name) with (list of objects) as a group on (object)” that automatically handles altering the multiple object list, and setting the noun and current item from the multiple object list.

(Oh, also, if you put one glass and the vase on the table you get “one glasses.”)

The glasses are just an example. I’m actually thinking of a situation where there are several identical objects, and putting one of them in a certain place gives a lengthy custom response. So putting several of them there at once prints the same lengthy message over and over again, which is what I want to avoid–in that case there should be just one custom message. I don’t really mind if short, standard messages are printed separately, as usual.

So it’s possible to just do “now the item is on the surface”–ok, for some reason I was thinking that wouldn’t allow the usual check rules and such to run, but if it does, that makes things a lot easier.

Thanks!

Thanks for your help as well, matt w–I will have to experiment some more. I think I did also try the “consolidated multiple actions” extension for this purpose but it seemed like it was meant for combining standard messages instead of custom ones…I don’t remember exactly, it’s been a little while since I tried it.

Well, it is, but you’re right in that it sidesteps all the check rules for putting things on things. So if you had a rule blocking the placing of vases on the table, what I wrote would indeed sidestep that restriction.

Yeah, the thing is that Actions on Groups always sidesteps all those rules. Unless you use “make no decision” to tell it to go through the usual action machinery–which includes the usual report machinery–then the Action on groups rule will do whatever you tell it to do and then stop the action processing completely. (If there’s an intuitive idea behind that, it’s something like this: If you’re doing a group action that puts the rock, the bun, and the boat on the table, which object do you run the Check rules for?)

That’s why I don’t super recommend using it for consolidated action reporting. There might be ways to do it that wouldn’t circumvent the machinery–you could cache the multiple object list, clear it, silently try putting the glass on the table, and then test if the glass is on the table–but it’d get complex. If your main purpose is to prevent multiple prints of a message you might just set a flag to see if the message has been printed once this turn–though if you want to the message to include the number of glasses you put on the table, and you might fail to put some of the glasses on the table, things could get complex.

For what it’s worth, I’ve been poking around with some very sloppy code for consolidating action reports:

Include Text Capture by Eric Eve.

Taking is summarized action.

The summary preparation rules are a rulebook. The summary report rules are a rulebook.

Stub announcement text is some text that varies.
Attempted list is a list of objects that varies.
Exception list is a list of objects that varies.
Actions being summarized is a truth state that varies.

An action-processing rule when the current action is summarized action and the multiple object list is not empty and action is not silent (this is the summary preparation stage rule):
	now the attempted list is the multiple object list;
	follow the summary preparation rules;
	stop the action.
	
The summary preparation stage rule is listed before the announce items from multiple object lists rule in the action-processing rulebook.

A turn sequence rule when the current action is summarized action and the multiple object list is not empty (this is the summary report stage rule):
	follow the summary report rules. 
	
The summary report stage rule is listed before the every turn stage rule in the turn sequence rulebook.

The before-generation rule is listed before the generate action rule in the turn sequence rules.

This is the before-generation rule:
	truncate the exception list to 0 entries;
	truncate the attempted list to 0 entries..
	
First summary preparation rule:
	let X be the current item from the multiple object list;
	now the stub announcement text is the substituted form of "[X]: ";
	start capturing text;
	try silently the current action;
	stop capturing text;
	let T be "[captured text]";
	unless T exactly matches the text "":
		add the current item from the multiple object list to the exception list;
		say "[stub announcement text][T]".

Summary report rule for taking:
	remove the exception list from the attempted list;
	say "You pick up [attempted list with definite articles]."

Lab is a room.

A rock, a roll, a boat, and a bun are in Lab. A mountain is fixed in place in lab.

After taking the roll: say "What a roll it is!"

If you want, maybe you could change the “unless T exactly matches” line to test for whatever complicated message your object prints–then you could use this to gather the number of items that printed your complicated message, and spit that all out at the end. This code is a mess, though; there are a lot of things in it that don’t serve any particular purpose.

Code that actually works, messy or otherwise, will be an improvement on what I’ve come up with. Thank you!

Oh, I could say the same thing! I was poking around with a way of making the syntax nicer for the kind of redirection that we need, and I realized that my latest code for the glasses broke the test script when I changed it from “Action on groups for putting glasses on the table:” to “Action on groups for putting something on the table:”, because I forgot to include a check that if there were no glasses on the table then the Action on Groups rule should stop. This meant that every time through the multiple object list the current item got changed to the vase, which is bad! Here’s a debugged version, with the new phrase up top for doing all the stuff you need to do when switching from one multiple action to another.

Include Actions on Groups by Matt Weiner.

To prepare to continue the action on/with (new list - list of objects), as nouns or as second nouns:
	alter the multiple object list to new list;
	let item be entry 1 in new list;
	if as second nouns:
		now the second noun is item;
	otherwise: [the "as the nouns" option doesn't do anything, but it's included for symmetry]
		now the noun is item;
	now the current item from the multiple object list is item;
	say "multiple object list: [multiple object list]. current item: [current item from the multiple object list]."
	
A glass is a kind of thing.
The player carries a pitcher, six glasses, and a vase of flowers.

Dining Area is a room.
A table is a supporter in Dining Area.

Test me with "put two glasses on the table / look / put three glasses and the pitcher on the table / look / take all / put four glasses and the vase and the pitcher on the table / look"

Putting something on something is a groupable action.

Action on groups rule for putting something on the table:
	let G be a number;
	let Extras be a list of things;
	repeat with item running through the multiple object list:
		if item is a glass:
			increment G;
			now the item is on the table;
		otherwise:
			add item to Extras;
	if G is 0:
		continue the action;
	say "You carefully set [G in words] glasses on the table.";
	if the number of entries in Extras is 0:
		rule succeeds;
	if the number of entries in Extras is 1:
		let item be entry 1 in Extras;
		clear the multiple object list;
		if G > 0:
			say "[item]: [run paragraph on]";
		try putting item on the table;
		rule succeeds;
	otherwise if the number of entries in Extras > 1:
		prepare to continue the action on Extras;
		continue the action.

I’m going to put that phrase in the extension, and if I may, try using your glasses code as an example. I also need to add a caution about how Actions on Groups rules shouldn’t specify the noun or second noun, as that won’t run unless the noun or second noun you want is the first item in the multiple object list.

In fact! I just thought of a way to streamline that:

To decide whether dealing with (OS - description of objects):
	repeat with item running through the list of OS:
		if item is listed in the multiple object list, yes;
	no.

This lets us write “Action on groups rule for putting things on the table when dealing with a glass:” which is much nicer than checking G in the body of the rule. That should definitely go in the extension.

Thanks for the ideas

It’s good. My ideal version of this would be that an Action on groups rule would delete the matching objects from the parse list, so that we’d write things along the lines of

Action on groups rule for putting a glass on the table:
	let L be a list of objects;
	repeat with item running through the multiple object list:
		if the item is a glass:
			try putting the item on the table;
			add the item to L;
	say "[We] [put] [number of entries in L] glass[unless L is exactly 1]es[end unless] on the table."

Action on groups rule for putting something on the table:
	let L be a list of objects;
	repeat with item running through the multiple object list:
		try putting the item on the table;
		add the item to L;
	say "[We] [put] [L with definite articles] on the table."

At this point, I’m hoping for added hooks in the new version, whenever it comes out.

I’m not particularly attached to it. :slight_smile: Do whatever you want with it.

You could change that to “silently try”, and add a phrase like “remove [something] from the multiple object list” (that would add it to a global list, to be subtracted from the multiple object list when the rule finishes). That would be a good general solution.

Draconis, how are you envisioning this part:

as working? I’m not sure where the hook for subtracting it would go… if I had a general phrase “remove [something] from the multiple object list” I’d have to make sure that at some point the list actually got subtracted. But there isn’t an obvious hook for that, because it doesn’t even happen in general that you wind up calling the action again with a revised multiple object list. Which means that we don’t want the stage rule that calls the Action on Groups rulebook to be doing anything with the multiple object list.

I guess one possible approach would be to have a Last Action on Groups rule that ran if anything had been removed from the multiple object list (this would be checked by a flag that got set by the “remove [something] from the multiple object list” phrase), and that prepared the multiple object list as the old list with the removal list subtracted from it. You’d have to make sure that the rulebook reached that rule if and only if you wanted to continue with the old action after removing items from the list… but that shouldn’t be too bad, because Action on Groups rules will stop the rulebook unless you tell them “continue the action.” It might require delicate handling, though. I’ll have to think about it.

Eleas, the thought of implementing “for putting a glass on the table” as opposed to “for putting something on the table when dealing with a glass” gives me the heebie-jeebies. When the action on groups rulebook runs at first the action is “putting [the first item on the multiple object list, whatever it is] on the table,” and I don’t think there’s any way to get Inform to figure out that the “putting a glass on the table” rule should fire whenever there’s a glass in the multiple object list. Unfortunately with my syntax I think you lose automatic specificity order, that is Inform won’t automatically sort “when dealing with the list of glasses” before “when dealing with a glass,” but we probably have to live with that.

In this particular case it seems like something that ideally would be taken care of by a list… if you can manage to come up with a list of the things you succeeded in putting on the table, then the list writer will automatically group the glasses for you. The problem is coming up with a sensible way to handle trying to put the things on the table… we don’t just want to print random failure messages without any explanation of what’s failing, but we don’t know whether to print the announcement from the multiple object list until we know if the action has succeeded. So we really need some kind of multiple action consolidation thing that finds a way to look ahead at what happens with the action before it starts printing, which Consolidated Multiple Actions does by invoking Hypothetical Questions (which in turn uses the undo buffer, I think), while the code I posted above somewhere handles it by capturing the output and then, if it’s something that can’t be consolidated, announcing the object and then printing the captured output.

Oh also! There’s just not going to be any way to call single actions from in the Action on Groups rulebook without clearing the multiple object list. Otherwise the action gets processed as an action on the whole list and we hit an infinite loop. So don’t do that. I guess I could try to write a hook that caches the multiple object list, runs the singular action, and then restores the multiple object list, but once we’re doing that I think we’re contorting the extension in ways it doesn’t really go.