Get all reveals hidden object - solution in PunyInform 3.3?

I noticed in my game that the player can type get all and it reveals a hidden object! In this case a kettle. Example:

Note the message you get as well. Luckily there is a new version of PunyInform (3.3) that should solve this! The notes read:

“Entry point routine ChooseObjects is now supported, meaning the game author can affect which objects are included in commands like TAKE ALL and which object is chosen when several objects match the input!”

Does anyone know how to use this, or any other method, to prevent the hidden object from being taken?

Also it should be takeable once the player has found it (so get all works as normal).

“Hidden” objects can be implemented in many ways, depending on the desired functionality. If the concealed attribute is used (perhaps the simplest way), the parser should not be including the object as part of its interpretation of “all”. (I just made a quick test with Puny 3.3 to confirm this.)

It would be easier to assist if you post the relevant portions of your source code, to better illustrate the issue. Screen shots of story interaction do almost nothing to indicate where the underlying problem might be.

3 Likes

Assuming the recent PunyInform update handles ChooseObjects(obj, code) in the same way as the Inform 6 standard library, then you can use the documentation in The Inform Designer’s Manual, 4th edn (or DM4 for short), specifically section 33 and appendix A5.

Essentially, ChooseObjects is called in two situations. If code is 0 or 1, the parser is considering including obj in an “all” command like GET ALL or DROP ALL. This is the one you’re interested in.

If code is 2, the parser is trying to disambiguate between two or more objects that match the command. When processing obj, this is an opportunity for you to say whether or not to include it in the current situation.

In your case, I suspect that you have either used an invent routine to prevent the kettle being listed parenthetically as an object on the table or (more likely) you have made it concealed. In both cases, GET ALL has done the right thing, as you have only prevented listing the object and there is nothing to stop you getting it.

Here’s an example of a ChooseObjects routine from a recent game of mine. It illustrates both situations where ChooseObjects is used.

[ ChooseObjects obj code;
  if (code == 2)
  {
    ! Stage 1: Disambiguation
    if (action_to_be == ##Close && obj has open)
      return 1; !Appropriate
    if (action_to_be == ##Drop && obj in player && obj hasnt worn)
      return 1; !Appropriate
    if (action_to_be == ##Lock && obj hasnt locked)
      return 1; !Appropriate
    if (action_to_be == ##Open && obj hasnt open)
      return 1; !Appropriate
    if (action_to_be == ##Take && obj notin player)
      return 1; !Appropriate
    if (action_to_be == ##Unlock && obj has locked)
      return 1; !Appropriate
    return 0; !Inappropriate
  }
  ! Stage 2: Processing an "all"
  if (obj has scenery or concealed or static or animate)
    return 2; !Force exclusion
  if (action_to_be == ##Take && obj ~= player && obj notin player)
    return 1; !Force inclusion
  if (action_to_be == ##Drop && obj in player && obj hasnt worn)
    return 1; !Force inclusion
  return 0; !Accept parser's decision
];
2 Likes

It does.

1 Like

The concealed attribute means something is hidden and the library should never give away the object’s existence. This means it won’t be included in ALL, and it won’t be included in disambiguation questions - if more than one object can match the input, only non-concealed objects will be considered.

Example from a slightly modified Testbench.inf:

Object -> Table "table"
	with
		name 'table',
		has supporter scenery;

Object -> -> "obvious plate"
	with
		name 'obvious' 'plate',
		article "an";

Object -> -> "concealed plate"
	with
		name 'concealed' 'plate'
	has concealed;

And then we play it:

Car Park
You are in a car park in front of the library. You can enter to the 
north. There is a river off to the east.

A little red car is parked here.

You can also see a key and a lead weight here.

On the table you can also see an obvious plate. 

> get all
obvious plate: Taken.
key: Taken.
lead weight: Taken.

> drop all
obvious plate: Dropped.
key: Dropped.
lead weight: When the lead weight hits the ground it sounds different.

> get plate
Taken.

> i
You're carrying an obvious plate.

> examine concealed plate
There is nothing special about the concealed plate.

> examine plate
There is nothing special about the obvious plate.
Object -> TableBlue "square table"
	with name 'square' 'table' 'wooden' 'squ' 'tab' 'woo',
	description "A square wooden table that fits nicely in the corner.",
    after [;
       if (WhiteKettle in self)
          move WhiteKettle to parent(self);
          "There's a white kettle on the table.";
    ],
	has scenery supporter;

Assuming the recent PunyInform update handles ChooseObjects(obj, code) in the same way as the Inform 6 standard library, then you can use the documentation in The Inform Designer’s Manual, 4th edn (or DM4 for short), specifically section 33 and appendix A5.

Great! Thanks for this. My kettle code uses has scenery supporter.

OK.

First, the way that you’ve written your after clause, it will be triggered by any action affecting the table. You probably want this response to be restricted to ##Examine actions.

Second, you’ve designated TableBlue as scenery and supporter. The scenery attribute means that the table will be omitted from the room description. It does not mean that the table or its contents will be unavailable for player interaction, or that its contents will be excluded from “all”. You can give the WhiteKettle the concealed attribute to “hide” it in a traditional way, i.e. someone who knows that it is there will be able to >TAKE KETTLE, but it won’t be included as part of “all”.

You may want something more like:

Object -> TableBlue "square table"
    with name 'square' 'table' 'wooden' 'squ' 'tab' 'woo',
    description "A square wooden table that fits nicely in the corner.",
    after [;
       Examine:
           if (WhiteKettle in self)
              give WhiteKettle ~concealed;
              "There's a white kettle on the table.";
    ],
    has scenery supporter;

Object WhiteKettle "white kettle" TableBlue
    with name 'white' 'kettle'
    has  concealed;

Note how examining the table removes the concealed attribute from the WhiteKettle object, so afterwards it will be considered part of “all”.

2 Likes

Ooh, that’s a bit messy. Your after routine is not associated with a specific action, so any action on the table moves the kettle to the room. For example, EXAMINE TABLE, GET TABLE, EAT TABLE all move the kettle off the table and into the room. That’s really, really old school, like The Quill era.

I somehow don’t think that’s what you want. You haven’t shown the code for the kettle. It might be easier if you explained what you’re trying to do. For example, if you want the kettle to initially be absent and the player discovers it after examining the table, make the kettle initially nowhere and try something like this:

Object -> TableBlue "square table"
with
  name 'square' 'table' 'wooden' 'squ' 'tab' 'woo',
  description
  [;
    if (WhiteKettle in nothing)
      move WhiteKettle to self;
    print "It's a square wooden table that fits nicely in the corner. ";
    <<Search self>>;
  ],
has scenery supporter;

This tests to see whether the kettle is currently in play. If it isn’t, it brings it into play. It then prints the description of the table and all the contents on the table.

2 Likes

That’s really, really old school, like The Quill era.

That’s where I started, back in the 80s. I think all my posts on here need a sign “Caution: Newbie to PunyInform making his first game”!

Here’s the code for the kettle:

Object -> -> WhiteKettle "white kettle"
	with name 'white' 'kettle' 'whi' 'ket',
	description "A white plastic kettle.",
	has container openable;

I think I’ll move the kettle out of the room until the table is examined. I got this to work yesterday in another room. I should have realised it should work here too.

if you want the kettle to initially be absent and the player discovers it after examining the table, make the kettle initially nowhere.

Yes, that’s the idea.

1 Like

I know this has been resolved already but I wanted to share an example how I did something very similar in Hibernated 1. Note that you get this easily running when considering the object hierachy in Inform 6. I often place hidden objects inside other objects (-> ->), which are then not available for interaction until a certain action is triggered. It kinda is what you proposed @chrishester by moving the kettle out of the room. When I started working with I6, I actually had a room called “limbo” in my code, and I moved objects from and to whenever I saw fit. This is pretty much the Quill / Paws way of doing it. The major disadvantage of using a limbo room: readability of your code. I6 is object oriented so whenever possible you want all objects in the object hierachy being together code-wise.

I think the below example will give you what you need. In H1DC, you have to search a messy desk to reveal your biometric pass. In the object hierachy, the pass is being placed inside the table and I move it to the room once the search action had been triggered by the player. Hope this helps. Note that this is “my” way of doing it. It may not be the best way and it may not be the most efficient way. But it works very well for me and I don’t have to deal too much with object attributes.

Object ->  messydesk "messy desk"
  with  name 'messy' 'desk' 'chaos',
    description "Such chaos is quite counterproductive in emergency situations.",
    before [;
        Push, Pull:
          "It's too heavy to move.";
        Search:
          if (~~(biometricpass in messydesk)) "You search again but don't find anything of interest this time.";
          move biometricpass to Privatearea;
          scope_modified = true;
          "You search for a while and finally find your biometric pass, the enemy of all locked doors on this ship.";
    ],
  has scenery;

Object -> ->  biometricpass "biometric pass"
  with  name 'biometric' 'pass' 'passport',
    initial "There's your biometric pass on the desk.",
    description "The biometric passport is a small key card that gives you access to all systems and areas on the Polaris-7.",
    before [;
      Insert:
        ! the player should also be able to PUT IN the pass or INSERT it to unlock the door
        if (second == securitydoor) {
          <<Unlock securitydoor biometricpass>>;
        }
    ],
  has scored;

Great, thanks for sharing some of your code! I’ve been placing all my hidden objects in a fake room called “Nowhere” and moving them into the player’s possession (or into the room) on examining or opening something. It avoids the objects being discovered by the parser.

I know you can use “nothing” instead of a fake room but I couldn’t get it to work.

I agree it means all the objects are not in the relevant rooms in the code, but that could make it easier to edit several objects at once. It’s swings and roundabouts.

I notice you have a Before test for the player typing Push or Pull for the desk. Instead of doing this for every object that’s too heavy to move, have you thought of using Classes? I put the code below into my game. Now all objects too heavy, fixed to the wall/floor, or not meant to be taken, can be dealt with just by replacing “Object” with the relevant class. I even allowed for plurals. Works a treat!

Like I said, this is a solution that works well for me but it might not work with the minds of others. It resolves your issue though, which I faced myself just by adding a second → by moving it out of the room inside an object in that’s located in the room. It’s a quite pragmatic solution but allows me to keep all objects in the place where you would find them in-game too.

Thanks, yes, I know about classes of course. I use them for different scenarios though. For instance, I do have two object classes for locations, one is called “Room” and one is called “Outside” I also have classes for NPCs which I defined as “Character” to apply interaction-specific standards to that class.

Concepts like the above though tend to make the game less vibrant as you will then cause many standard responses instead of object-specific ones. When you try to push a certain door in my game you get a response that is much like this “There is no way you could open the door just by pushing it.” I do think the “Class Fixed” class in your example would be obsolete in my own code, as “(The) noun is fixed in place.” is the standard response from Puny when you apply the “scenery attribute” to your object. If I would want to implement such a concept as the above, I probably would override Puny’s standard scenery message and define additional object attributes:

if (object has leave) do something

You can also set up a set of standard messages with string constants.

But the thing to consider is not how I would do it, the thing is the code needs to work best for you and your own brain, that’s the power of Inform, many ways lead to where you want your game to be.

You said that you couldn’t get the test for nothing to work. That’s because the WhiteKettle wasn’t in nothing. It was a sub-sub-object of something else. To fix that, change the code as follows:

Object WhiteKettle "white kettle"
with
  name 'white' 'kettle',
  description "It's a white plastic kettle.",
has container openable;

For what it’s worth, I NEVER use the -> notation for placing objects. It’s much less confusing to just put the parent after the object description. For example, if the food scraps are on the plate, but you don’t want them to be initially seen, and the plate is on the kitchen table and the kitchen table is in the kitchen, then the object definitions would be something like this:

Object kitchen "Kitchen"
...
Object table "kitchen table" kitchen
...
Object plate "porcelain plate" table
...
Object scraps "food scraps"
...

In this way, you can move objects around in your code without getting horribly confused with the level of ‘->’ that you need. Note that the food scraps don’t have a parent, so they’re initially in nothing.

2 Likes