Reducing the possibilities for guessed key?

OPTIONAL_GUESS_KEY in Punyinform is great for picking the right key when a player has two keys. However, if the player doesn’t have the right key, it makes a guess the missing object, and often returns stuff like the robe the player is wearing or other not-a-key things.

I can make a Key class so it’s easy to tell if something is a key, but I don’t see a straightforward way to reduce guesses to those without copy/paste/modifying all the library code.

Is there a way to catch to catch attempts to lock/unlock before “(assuming with ___)” gets printed? If I can, I can add sanity checks for could-be-a-key. It doesn’t even call UnlockSub before it prints that.

1 Like

It doesn’t call LockSub since it is happening while parsing the input (figuring out what the noun is).

Improving the behavior probably requires replacing or modifying the _GuessMissingNoun routine.

3 Likes

Thanks, @andrewj . I was hoping I was missing something easier, but it wasn’t too hard to write something that worked.

[ _GuessMissingNoun p_type p_prep p_nounphrase_num _i _noun ;
  ! If the player doesn’t have a key, don’t guess for lock/unlock
  !  otherwise, made really strange choices like your robe
  if (action == ##Unlock or ##Lock) {
    for (_i = 0: _i < scope_objects: _i++) {
      _noun = scope–>_i;
      if (_noun == LibraryKey or GardenGateKey)
        return Original_GuessMissingNoun(p_type, p_prep, p_nounphrase_num);
    }
    return 0;
  }

  return Original_GuessMissingNoun(p_type, p_prep, p_nounphrase_num);
];
4 Likes

I wasn’t aware of the _GuessMissingNoun routine. I always do this:

Step 1: Before including globals.h, tell the library that the following two routines will be replaced.

Replace LockSub;
Replace UnlockSub;

Step 2: Somewhere after including puny.h, replace those routines.

[ LockSub k;
  if (ObjectIsUntouchable(noun))
    rtrue;
  if (noun hasnt lockable)
  {
    PrintMsg(MSG_LOCK_NOT_A_LOCK, 'lock');
    rtrue;
  }
  if (noun has locked)
  {
    PrintMsg(MSG_LOCK_ALREADY_LOCKED);
    rtrue;
  }
  if (noun has open)
  {
    PrintMsg(MSG_LOCK_CLOSE_FIRST);
    rtrue;
  }
  k = RunRoutines(noun, with_key);
  if (k == nothing)
    "You can't see how to lock ", (ItOrThem)noun, ".";
  if (second == nothing && k ~= nothing && k in player)
  {
    second = k;
    print "(with ", (the)second, ")^";
  }
  if (second == nothing)
    "You don't have the key.";
  if (second ~= k)
  {
    PrintMsg(MSG_LOCK_KEY_DOESNT_FIT);
    rtrue;
  }
  give noun locked;
  run_after_routines_msg = MSG_LOCK_DEFAULT;
  run_after_routines_arg_1 = 'lock';
];

[ UnlockSub k;
  if (ObjectIsUntouchable(noun))
    rtrue;
  if (noun hasnt lockable)
  {
    PrintMsg(MSG_UNLOCK_NOT_A_LOCK, 'unlock');
    rtrue;
  }
  if (noun hasnt locked)
  {
    PrintMsg(MSG_UNLOCK_ALREADY_UNLOCKED, 'unlock');
    rtrue;
  }
  k = RunRoutines(noun, with_key);
  if (k == nothing)
    "You can't see how to unlock ", (ItOrThem)noun, ".";
  if (second == nothing && k ~= nothing && k in player)
  {
    second = k;
    print "(with ", (the)second, ")^";
  }
  if (second == nothing)
    "You don't have the key.";
  if (second ~= k)
  {
    PrintMsg(MSG_UNLOCK_KEY_DOESNT_FIT);
    rtrue;
  }
  give noun ~locked;
  run_after_routines_msg = MSG_UNLOCK_DEFAULT;
  run_after_routines_arg_1 = 'unlock';
];

Step 3: Extend the grammar.

Extend 'lock' first
  * noun -> Lock;

Extend 'unlock' first
  * noun -> Unlock;

Let’s assume the locked object is a door. The end result of all this is that if you enter UNLOCK DOOR, it no longer gives you that stupid message about ‘I think you wanted to say “unlock door” with something.’ Instead, it takes a look at the with_key property to work out what key to use and if you’re carrying it, then it implicitly uses that key, otherwise it says you don’t have the key.

The library changes are negligible, but the benefit to the player is enormous. I include this code in the skeleton I use for every single game, so I don’t have to think about it.

If you have an object that looks like it’s unlockable, but it isn’t because its key isn’t in the game, just create a dummy key:

Object dummy_key;

and put that in the with_key property for the locked object:

with_key dummy_key,

The only shortcoming that I can think of is that this might only work for one key per lockable object. If you have an object that can be unlocked by either of two keys, say a front door key and a skeleton key, then you might need to do something special, but I’ve never struck that situation.

3 Likes

Thanks, @Warrigal !

Just put your code in and it works great, and is a much better solution — in this game, the keys look very different, and the player already knows what they’re for, so it’s perfectly reasonable for them to know the garden with a big rusty lock would use the tiny silver key.

1 Like