I6 + Puny: Listing individual objects as a group in the same way as listed in room description

I have three separately defined bird cages. Inform describes them after the room description like so:

You can see a green bird cage (which is open but empty), a blue bird cage (which is open but empty), a red bird cage (which is open but empty)…

I would like the player to be able to examine cages and get the same text. (At the moment the command results in “You can’t use multiple objects with that verb.”) Is there a routine I can call that produces that text?

I don’t know of anything better, but in my game where I have this kind of things, I use a concealed scenery object with the name ‘cages’, and you can then do whatever you want in your Examine action for that.

If you have ‘cages//p’ in the name for each cage, you might need to bump up the priority of ‘cages’ to mean the all-cages-object, which you can do with something like:

[ ChooseObjects obj code ;
   if (code == 2) {   ! looking for priority of a match
      if (obj == AllCages && action == ##Examine) return 3;
      return 2;
   }
  else {
     rfalse;
  }
];

When I’ve done that, I’ve saved an object and made the “all-x” objects using cheap scenery (since in my case, the all- things weren’t fakeable or movable, so I could just add them as room-scenery with cheap_scenery). If the cages could be taken, you could just make a cage have an add_to_scope to pull in the all-cages object.

If you’re not experienced with a ChooseObjects hook, make sure you don’t forget the “return 2 as default for code==2”; if you forget that, you can be downgrading every non-cages match to lower-than-default-priority. And don’t forget the “else rfalse”, since it would otherwise return true (1), which isn’t the right default response for choose objects codes 1 or 2.

What generates the text “green bird cage (which is open but empty)…” Is there a routine I can call that will produce that?

(Cage class is defined with has container openable transparent open ~static large;)

In my case, I didn’t want the exact format as shown in the room description, but wanted it a bit shorter and more specific, so my AllBarrels object has this:

    before [ b i ;
      Examine:
        print "You see five barrels: ";
        objectloop (b ofclass Barrel) {
          i++;
          print (address) b.simple_name;
          if (b has open) print " (open)";
          switch (i) {
            1,2,3: print ", ";
            4: print " and ";
            5: ".";
          }
        }
    ],

However, in my case, that correctly assumes that all barrels are in this room. If you want to do it like this, you’d want an in-loop check if it’s in the room (or held by the player? Dunno if you’d want them shown)

I don’t know If there’s a simple solution to simply use the room description function, since that would include other, non-cage things, in the output.

But you can:

  • loop over everything in scope, and set its workflag bit to false (if not a cage) or true (if is)
  • Use PrintContents("You see ", YourRoom, WORKFLAG_BIT) which only lists the items with the workflow attribute set. PrintContens return 0/1+, telling you if none were found, so if it returns false, you can say something like “There are no cages here.”

If you’re unfamiliar, “loop over scope” can be done with LoopOverScope(MyCallbackRoutine) – but note that that will find every object in scope; if you want to limit to just in-room or directly-held-by-player or such, you’d want a discriminant in the callback routine, something like

[ MyCallbackRoutine obj ; 
   if (obj ofclass Cage && parent(obj) == location or player) give obj workflag;
   else give obj ~workflag;
];

(that assumes you have a “Cage” class; if not, put in the logic to see if it’s equal to RedCage or BlueCage or PinkCage or whatever…)

The object list is produced by PrintContents. This routine uses an internal routine called _PrintAfterEntry to print the part that comes after the object name. However, when this happens, the global c_style must be set to define the type of listing you want, or the results will be unpredictable. For your use case, you’d just set it to 0.

E.g.

print (a) Jar; c_style = 0; _PrintAfterEntry(Jar);

This can produce something like:

a glass jar (which is closed and contains a pea)

This is an internal/private library routine, which means:

  • Its name starts with _
  • It’s not mentioned in the documentation
  • It’s not explicitly intended for use directly by game authors, but it’s in no way forbidden or considered really bad form to use it anyway
  • We may change the way it works, rename it or even remove it in future versions of the library, and we may not mention this in the “Important to note when upgrading” part of the release notes when we do so
  • We do however mention changes even to internal routines in the release notes, so if you read through all of the release notes when upgrading the library, you should be able to spot it
2 Likes

I’m not sure ChooseObjects is having any effect. My AllCages object:

    Object AllCages "cages"
    with
      name "cages//",
      found_in Shop
    has scenery;

If examine “cages”:

> x cages
There is nothing special about the cages.

What am I doing wrong?

shouldn’t that be?

if (code < 2)...

Ah, it wasn’t being called. I had originally separated my entry points into lib/entry_points.h . My includes looked like this:

Include "./lib/entry_points";
Include "puny";
Include "./lib/objects";
!...

I’ve removed the entry_points include and redefined ChooseObjects before including puny. I’m unsure why this way works but not the other.

shouldn’t that be?

if (code < 2)...

No, @improvmonster ; this code is trying to decide the parse-priorities of a single object (at least how I wrote it), bumping up AllCages when examined to higher-than-default 3. This use case for ChooseObjects is code==2.

I guess you could write it for code==0 or ==1 (which is about including/excluding matching items), but I’d think this would be a bit more code.

1 Like

PunyInform requires you to define all entry point routines except initialise before including puny.h. This requirement helps keep the size of the compiled game down, and keep the speed up.

If you define an entry point routine too late in your code, you should see a warning that the routine isn’t used.