How do I print a custom message in reponse to SCORE for a PunyInform game with no scoring?

I have some unpublished Inform 6 games that I’m thinking of porting to PunyInform. Most of these have a few custom messages to replace those supplied by the standard library. The first one I encountered is the response to the SCORE command when the game doesn’t have any scoring. With the standard library, I do this:

Constant NO_SCORE;
Object LibraryMessages
with
  before
  [;
    Score:
      if (lm_n == 2)
        "There's no score in this game. Your objective is to blah...blah.";
  ];

If I declare the NO_SCORE constant in PunyInform, the SCORE command is not understood at all. Personally, I think this is wrong. It should just print a default message and allow you to overwrite the default message if you choose to, the same as the standard library.

So, let’s omit the declaration of the NO_SCORE constant and replace the PunyInform response. After experimenting with the static strings and dynamic strings for the custom library messages, the closest I could get was this:

Constant MSG_SCORE_DEFAULT 1000;
[ LibraryMessages p_msg p_arg_1 p_arg_2;
  switch (p_msg)
  {
    MSG_SCORE_DEFAULT: "There is no score in this game. Your objective is to blah...blah.";
  }
  p_arg_1 = p_arg_2;
];

This prints the desired message, but then prints an extra period on the next line. What the?

After trawling through grammar.h, I found that ScoreSub was responsible for printing the default message (as expected), but it doesn’t print the extra period:

[ ScoreSub;
  PrintMsg(MSG_SCORE_DEFAULT);
  PrintRank();
];

It’s the code for PrintRank that looks a bit iffy:

#Ifndef NO_SCORE;
#Ifndef PrintRank;
[ PrintRank; "."; ];
#Endif;
#Endif;

I did not have the PrintRank entry point routine defined, but it went ahead and tried to execute it anyway, then printed a period. This looks like a bug to me. #Ifndef should be #Ifdef and it should not print the trailing period. That is the responsibility of the PrintRank routine.

EDIT: I spoke too soon. I should have tested this first. I think I can see what it’s trying to do. If the PrintRank routine is not defined, then it defines it and all it does is print a period. This is certainly not the right thing to do. A better solution would be to modify the ScoreSub routine as follows:

#Ifndef NO_SCORE;
[ ScoreSub;
  PrintMsg(MSG_SCORE_DEFAULT);
  #Ifdef PrintRank;
  PrintRank();
  #Endif;
];
#Endif;

Please feel free to correct me if I’m wrong, as I probably am wrong. :laughing:

While trawling through grammar.h, I also found an issue with the grammar for FULLSCORE:

Verb meta 'fullscore' 'full'
    * -> FullScore
    * 'score' -> FullScore;

This expands to 'fullscore', 'full', 'fullscore score' and 'full score'. I’m being pedantic, but the third one is incorrect.

PunyInform only provides MSG_SCORE_DEFAULT which works like Score/lm_n 1 in I6. Even in I6 this message won’t add the period, allowing the ranking to add this. LIke in PunyInform. However, PunyInform currently doesn’t have anything similar to the lm_n 2 message which you want to redefine. This is arguably a bug and we can add this in a future release. For now you can solve it by adding a PrintRank that does nothing.

As for the fullscore verb then obviously the third is incorrect, but splitting it into two verb definitions takes more space, and I can’t imagine it to be a problem in practice. So I’m leaning towards wont-fix on that.

1 Like

Thanks @johanberntsson. I can either use Replace ScoreSub; and add my own ScoreSub routine or add a dummy PrintRank routine for now. In either case, if I keep it localised, I can delete it later. Your solution is probably simpler.

(We’ve already sorted this out on the Discord server, but let’s put it here as well for completeness)

If you define NO_SCORE but still want the score verb to work and say there’s no scoring, you can do:

Constant NO_SCORE;
Verb 'score'
  * -> Score;
[ScoreSub;
  "There is no score in this game.";
];

This works in any recent PunyInform version. We may provide this functionality by default in some future version.

2 Likes

We agree with Graham Nelson’s philosophy that the parser’s job is to understand as much as possible of sensible input, and it’s okay if it sometimes accepts less sensible input. If the player thinks of typing “fullscore score”, then let them. The alternative is for us to split this up in different grammar tables, a solution that makes the game larger for no real benefit of the author or the player.

There are more examples of this happening, both in the standard library and PunyInform.

1 Like

This would have been my choice as well and it is a very obvious solution. Actually I did something similar with UNDO. The z5 versions of my games support the UNDO command, while the z3 versions don’t. Still, I wanted UNDO at least somehow to throw something out in z3. So I’ve set up a conditional constant

#ifV5;
Constant OPTIONAL_PROVIDE_UNDO; ! undo opcode is only supported in Z-machine v5 spec
#endif;

and added the verb

#ifV3;
Verb 'undo' * -> Undo;
#endif;

and of course the sub.

#ifV3;
[ UndoSub;
  "This version of the game does not support the UNDO command.";
];
#endif;

So now, if I am playing on a machine that doesn’t support z5, the player gets at least a feedback.

Anyway, just a statement from my side that I think your solution is the best one for the scenario that triggered this thread and that I would go the same route.

1 Like

Rather than posting screenshots of code, it would be more accessible if you just copied the code and put them in code blocks. I’ll edit your post.

Sigh, you’re right! Let me edit it…

EDIT: you were faster. Thanks, I vow to do better :vulcan_salute: :heartbeat:

1 Like