Silent mode where "say" does nothing

I would like to be able to set a flag that silences the “say” command. This would speed up some tests I’m trying to run.

Ideally, something like this:

now silence_flag is 1;
say "Silence.";
now silence_flag is 0;
say "Hello.";

would only print “Hello.”

Any suggestions how to do this?

This may or may not suit your purposes, depending on what you’re trying to do:

Conditional saying is a truth state that varies. Conditional saying is false.

To consay (M - a text):
	if conditional saying is true:
		say M;
		[say paragraph break;]
	
Instead of examining the player:
	now conditional saying is true;
	consay "Not bad!";
	now conditional saying is false;
	consay "(If you don't look too closely.)";

This isn’t exactly what you want, because it doesn’t really affect the behavior of “say” per se(!), but if you find/replace “say” with “consay” throughout your source you get the same effect. Except line breaks might be weird. (That “say paragraph break” line is probably desirable, but it depends on your specific needs.)

On the other hand, this method does let you switch between “say” phrases and “consay” phrases so that some text always prints while other text is conditional and that might be useful!

Thanks – I tried something similar, but unfortunately it causes line break problems. Also I’d like to keep from changing all the “say” commands if possible.

I was thinking there might be a way to override an I6 function or something. (I’m not sure where to look.)

On the Z-machine, you can turn off the output stream that goes to the screen, which will cause all printing to just be lost to the void until you turn it back on. (Or be sent to the transcript but not anywhere else, when the transcript is turned on; that’s a separate output stream that can also be turned off, if you want.)

[ OutputSilence ;
    @output_stream -1;
];

[ OutputUnsilence ;
    @output_stream 1;
];

On Glulx it’s a bit more complicated, because in Glk it’s illegal to print anything when there’s no current output stream. I think the best way to do this is to make a dummy stream that throws away everything sent to it, and redirect to that:

Global silence_stream;
Global previous_stream;

[ OutputSilence ;
    silence_stream = glk_stream_open_memory(0, 0, 1, 0);
    previous_stream = glk_stream_get_current();
    glk_stream_set_current(silence_stream);
];

[ OutputUnsilence ;
    glk_stream_set_current(previous_stream);
    @copy $ffffffff sp;
    @copy silence_stream sp;
    @glk $0044 2 0;
];

The three assembly instructions in that last routine are because Inform’s template, for some reason, doesn’t include a wrapper for the stream_close Glk function.

Then you’d just need a way to call these from I7:

To silence all output: (- OutputSilence(); -).
To unsilence all output: (- OutputUnsilence(); -).

Warning, this is completely untested. But it should accomplish exactly what you want. (If this proves useful, I can test it more, polish it up, and put it in an extension.)

To avoid messing up the paragraphs, you can use this instead:

To consay (M - a text) -- running on:
	if conditional saying is true:
		say M;

Another option is to Include Text Capture by Eric Eve. and then you can start capturing text to redirect the “say” output to an internal buffer rather than immediately printing it, which would have the effect of silencing it if you discard the buffer without using it. (This is the I7 equivalent of Draconis’ @output_stream suggestion.) The capture buffer is fairly small by default (although can be enlarged) – although on Glulx overflowing the buffer is relatively harmless; it just throws away the extra characters, which is what you want anyway.

Include Text Capture by Eric Eve.

[...]
	start capturing text;
	say "This won't get printed.";
    stop capturing text;
[...]

This is not as good as avoiding actually saying it in the first place (e.g. with consay, or otherwise skipping the rule that says the text), however.

Alternatively, if the text in question is associated with reporting the results of an action, then you can put it into a Report rule – if the action is tried “silently” then the Report rules are not executed.

I’m trying the Glulx version. So far it works and I haven’t had any problems. (And the test now runs about 10x faster.) Thanks!

Glad it’s working! Tossed it in an extension for maintainability if necessary.

The glk_stream_close function definitely exists…

Yeah, please clean up your memory streams after use. Only you can prevent florist friars.

Is it available within the I7 templates? (My saying that it’s not available is mostly because of looking at Glulx.i6t, where wrapper routines like glk_stream_open_memory are explicitly provided, but glk_stream_close is just implemented wherever it’s needed by pushing arguments and calling the @glk opcode directly (so no wrapper used). Which makes me think not all of infglk.h is included.)

Yeah it’s there in Glulx.i6t. In fact the only function I can see which makes a manual Glk call to stream_close is Glulx_PrintAnyToArray.

(Slightly less disjointed response than the previous one. Sorry about that.)

The Text Capture extension and Glulx_PrintAnyToArray both make manual Glk calls because they want to retrieve the length of captured text from the return values – which should also be possible with the wrapper but is less convenient, since you’d have to get the results via an array rather than the stack.

It appears that Output Silencing requests the same thing (by passing $ffffffff rather than 0) but it’s not actually pulling the results off the stack afterwards, leaving it unbalanced. I’m not sure if that does anything dire in I6, but it’s probably not the best idea. (I also prefer using @push/@pull over using @copy as the latter is both longer and less portable, but that’s a stylistic choice.)

On a related note, it appears that the Glk and Glulx specs disagree on whether it is legal to print to a null stream or not. The Glulx spec says that’s it’s ok to print to a null stream (and would simply drop the output, as is the goal here) but not legal for a not-yet-set stream, while the Glk spec says that neither are legal since they mean the same thing. I assume the Glk spec is more correct in this regard though.

Oh yeah, I forgot about the return value. Not being able to return a struct/tuple from a function in I6 does mean that if you need the length then a direct @glk call makes sense.

The extra stack values are discarded when the I6 function exits.

You’re referring to this line?

(It is illegal in Glk to send output when there is no stream set. Sending output to Glulx’s “null” I/O system is legal, but pointless.)

That’s not a contradiction. It’s legal to print text after @setiosys 0 0; . In that case, the text doesn’t go to a null stream; it doesn’t go anywhere at all.

This is a good way to handle silencing, actually. I’d forgotten that was an option.

I assumed that (otherwise someone would have noticed the VM crashing). But it’s still bad style to ask for the values and then not deal with them rather than asking to not get them in the first place.

Ah. Yes, that was the line I was looking at, but I hadn’t “clicked” on that specific concept yet.

Ahh, yep, that’s my mistake. I copied most of the code from Glulx_PrintAnyToArray but didn’t think about the return value. I’ll fix that.