Can sizes of objects be set to prevent big going into small?

I am writing a game in PunyInform and have a plastic chair and a pedal bin in a room. It’s possible to open the bin and put the chair inside it! Clearly this is impossible in reality. So is there a way to set the size of an object to prevent this? Eg: from large, medium or small. Or using a numerical range.

See the screenshot for an example of the problem.

Yes, one way would be to specify a custom property called (e.g.) size on each object.

For example:

Object chair "breakfast chair" example_room
    with
		name 'chair',
        size 2;    ! 0 = small, 1 = medium, 2 = large

Object bin "red pedal bin" example_room
   with
		name 'bin',
		before [;
			Receive:
				if (noun.size == 2) {
					print_ret (The) noun, " is too bulky to fit in ", (the) self, ".";
				}
		],
	has container open;

This could be extended by defining a class containing a default value for size (e.g. medium), and creating objects from that class. That means you would only need to manually specify the size property on an object if it was especially large or small.

2 Likes

Brilliant, thanks!

1 Like

All sound advice.

If you’re using PunyInform, chances are you want to keep everything small and fast. An alternative to making all objects belong to a class would be to check if the object provides the size property and use a default value otherwise.

[ ObjectSize obj;
	if(obj provides size) 
		return obj.size;
	return 2;
];

Object bin "red pedal bin" example_room
   with
		name 'bin',
		before [;
			Receive:
				if (ObjectSize(noun)  > 1) {
					print_ret (The) noun, " is too bulky to fit in ", (the) self, ".";
				}
		],
	has container open;
3 Likes

Absolutely! I’d also add that if it turns out there are only a handful of objects in the game that are large enough not to fit inside the bin, it may be best to just specify them directly and save any extra overhead:

Object bin "red pedal bin" example_room
   with
		name 'bin',
		before [;
			Receive:
				if (noun == chair or lawnmower or oil_painting or acoustic_guitar) {
					print_ret (The) noun, " is too bulky to fit in ", (the) self, ".";
				}
		],
	has container open;
1 Like

Actually I do have an oil painting in the game, but it’s fixed to the wall. The problem with the latter suggestion is that there are multiple pedal bins in the game, so the code would be repeated. But there’s really not many large objects you could try to bin, a couple of chairs might be all. Other large items are just scenery.

If only there was a size variable built in to PunyInform…

You could create a class and have the Receive before code as part of it, then derive all the pedal bins from that, so the code would only be defined in one place.

I’m not familiar with Receive, only Before and After, usually followed by Examine. What does Receive do?

Receive is an action that happens when the player types something like:

put sandwich in bin

Basically, in the above command, the pedal bin “receives” the sandwich. So a Receive action is triggered on the pedal bin, and the noun is the sandwich.

If we want to stop the normal behaviour of the library (i.e. the sandwich will be put inside the bin), we block it by defining a Before property on the bin. Because we want to change what happens when something is put inside the bin, we need to block the Receive action within that Before property.

So all in all, you could have something like:

Class PedalBin
	with
		before [;
			Receive:
				if (noun == chair or lawnmower or oil_painting or acoustic_guitar) {
					print_ret (The) noun, " is too bulky to fit in ", (the) self, ".";
				}
		],
	has container open;

PedalBin red_bin "red pedal bin" example_room
   with
		name 'red' 'bin';

PedalBin blue_bin "blue pedal bin" example_room
   with
		name 'blue' 'bin';

Both bins will be blocked from normal library behaviour whenever something is put inside them, because the before property in their class traps the Receive action. Any additions or removals of large objects only need to be changed in the PedalBin class definition.

That’s great thanks! I wondered how to do classes too. But why not trap the action using just the verb used? I see this format in the Inform Beginner’s Guide:

before [;
Drop: print_ret “Much too dangerous…”

They also use commas to list several verbs such as Drop, Give.

Or is it that Receive traps everything the player types? Such as DROP CHAIR IN BIN or THROW CHAIR, PUT, PLACE etc.

As you can tell I am still learning…

Let’s say the player types “put hammer in box”

The direct object (noun) gets a chance to react first - the library calls Hammer.before() with action == ##Insert. If the hammer doesn’t have a before routine, or the before routine returns false, InsertSub will be invoked. InsertSub temporarily changes action to ##Receive and calls before for the indirect object (second) - in this case it calls Box.before(). If that routine doesn’t exist, or it returns false, InsertSub proceeds with the action. The After stage is similar.

This allows you to put a before routine in a container to react whenever anything is put into that container, instead of putting a before routine in every object in the game to check if they’re being put in the container.

There is also a ##LetGo action which is used when the player tries to take something from a container.

##Receive and ##LetGo are so called Fake Actions, meaning there are no lines in the grammar leading to these actions, and they don’t have their own action routines (i.e. there’s no ReceiveSub). They are issued by InsertSub and PutOnSub. When these fake actions are used, you can also check the receive_action variable to see which one of these is the current “main” action. Like:

Object Shelf "shelf"
  with
    name 'shelf',
    before [;
       Receive:
         if(receive_action == ##Insert) 
           "You can't put stuff IN the shelf!";
    ],
  has supporter;
1 Like

You mention PutOnSub. Is that used for supporters? I was going to ask about those next.

Yes.

“put hammer on shelf” → PutOn
“put hammer in box” → Insert