[I7] Any way to detect the failure of a "switching the story transcript on" action?

While playing around with something else, I noticed the following after entering >TRANSCRIPT and then canceling the file dialog window:

>TRANSCRIPT
[switching the story transcript on]
Attempt to begin transcript failed.
[switching the story transcript on - succeeded] ! succeeded?

(Similarly for canceling after >SAVE.) Would it be possible for the story to detect when this scenario occurs? Looking at the relevant code blocks, it seems like it might be…

[ SWITCH_TRANSCRIPT_ON_R; ! Z-code version
    if (actor ~= player) rfalse;
    transcript_mode = ((0-->8) & 1);
    if (transcript_mode) { SWITCH_TRANSCRIPT_ON_RM('A'); new_line; rtrue; }
    @output_stream 2;
    if (((0-->8) & 1) == 0) { SWITCH_TRANSCRIPT_ON_RM('C'); new_line; rtrue; } ! failure case
    SWITCH_TRANSCRIPT_ON_RM('B'); new_line; VersionSub();
    transcript_mode = true;
];

[ SWITCH_TRANSCRIPT_ON_R; ! Glulx version
    if (actor ~= player) rfalse;
    if (gg_scriptstr ~= 0) { SWITCH_TRANSCRIPT_ON_RM('A'); new_line; rtrue; }
    if (gg_scriptfref == 0) {
	    gg_scriptfref = glk_fileref_create_by_prompt($102, $05, GG_SCRIPTFREF_ROCK);
	    if (gg_scriptfref == 0) jump S1Failed;
    }
    ! stream_open_file
    gg_scriptstr = glk_stream_open_file(gg_scriptfref, $05, GG_SCRIPTSTR_ROCK);
    if (gg_scriptstr == 0) jump S1Failed;
    glk_window_set_echo_stream(gg_mainwin, gg_scriptstr);
    SWITCH_TRANSCRIPT_ON_RM('B'); new_line;
    VersionSub();
    return;
    .S1Failed;
    SWITCH_TRANSCRIPT_ON_RM('C'); new_line; ! failure case
];

but I haven’t been able to work out a way to force a failure of the action to register. (Perhaps because it’s an out of world action?)

A easy (and hacky) way to detect the failure of such actions (same fore SAVE and RESTORE), is to hook on the failure response.

Transcript failed is initially false.

After issuing the response text of the switch the story transcript on rule response (C):
	now transcript failed is true.

If you really want to modify the I6 template instead, I know it’s possible to run a rule or an activity instead of showing the response. You’d have to change the line marked with ! failure case.

[In I7]
Failing to start a transcript is an activity.

[In the Inform 6 template.
(Beginning of the routine cut for brevity.)]
[ SWITCH_TRANSCRIPT_ON_R; ! Glulx version

    ...

    .S1Failed;
    CarryOutActivity( (+ Failing to start a transcript +) ); ! failure case
];

(Z-code version left as an exercise to the reader. :stuck_out_tongue:)

I haven’t tested it right now, it’s based on an old extension I wrote, which worked at the time I wrote it.

3 Likes

In I7’s default model, an action “succeeds” if it gets to the carry out stage, regardless of what happens during carry out, after, and report. So in that model, there’s no way for e.g. the player hitting “cancel” in the save dialogue to cause the action to fail: if it’s calling the @save opcode at all, it’s already succeeded.

The simplest solution I can think of is to have a global flag that tracks success and failure for all these things that the interpreter handles (transcripting, saving, restoring, etc). But most of the time what you’ll want to do with that value is print a custom message—and that can be handled already within the Response system.

(As a side note, the one place the “success” or “failure” of an action actually matters iirc is for the “unsuccessful attempt” rulebook, which is called when you tell an NPC to do something and the action fails (never reaches the carry out stage). And that’s not relevant for out-of-world actions, since NPCs can’t attempt those anyway.)

1 Like

Some precisions on what Draconis said (if I’m not mistaken), even if a bit off-topic.

That’s what is officially told by the documentation, but I think all that matters is that one of the action rulebooks succeeds? If I recall correctly, making an Instead rule succeed will mark the action as successful.

It also matters if we are testing an action in the past, e.g. if we have taken the lamp. If an instead rule intercepted the taking the lamp action, the condition will be false.

(That’s why it can be useful to make an Instead rule succeed. If we use an instead rule only to print a special message, but the action can be considered to have succeeded “in-world”, you can make the Instead rule succeed. Or you can never use a condition in the past because it’s confusing and bugs will be created.)

1 Like

If you’re wanting the original action to succeed as normal but print a different message, then you should be making an After rule (or a Report rule, but an After rule is safer), not an Instead rule.

1 Like

Of course, but what about actions that are blocked by default, but you still want to handle it in some specific cases?

Instead of burning the paper:
    say "You burn the paper.";
    now the paper is nowhere;
    rule succeeds;

It’s not worth to remove the block burning rule only for this one special (if a bit contrived) case. But the action has succeeded “in-world” (the paper really has been burnt).

But again, I generally don’t recommend relying on whether or not an action succeeded. It can cause some bugs if you’re not careful.

For actions that have no standard Carry Out rules at all, maybe. But even there, I tend to prefer making it go through the “normal” action flow. And I unequivocally favour doing this for actions that do have a standard Carry Out even though they’re blocked by default (such as giving it to).

The block burning rule does nothing when the noun is the paper.
Carry out burning: now the noun is nowhere.
Report burning: say "You burn [the noun]."

Now if you want to define a second object that can be burned, all it takes is to repeat just the first line with the new object. And you can define extra After rules for special effects or messages (if burning a pile of leaves reveals something underneath, for example).

@Natrium729, thank you for this! I had tried to set up something similar with an after rule for the action itself, but after rules for out of world actions are disallowed in 6M62. Very clever to use the response activity rules.

I was really hoping for something that would allow forcing genuine failure of the action (for the purposes of keeping coherence with built-in action success tracking), but this is a useful workaround.

Also, I’ve observed the same as you with respect to being able to force success/failure by calling rule succeeds or rule fails at non-standard points in the action processing rules. (Not a bug, in my book.) Like you, I sometimes find it useful for keeping the built-in action success/failure tracking consistent. (Though I generally adhere to the style that @mirality espouses.)

In this case, even trying to modify the failure point with an I6-level RulebookSucceeds(); calls didn’t work. (Perhaps I didn’t do use that routine right; I didn’t trace things very far.)