Readthrough mode available for TADS games

Indeed it does. You have only to call savepoint() and the game’s state is saved to the VM. The next time that any code calls undo(), the state at the last savepoint will be restored. In my approach (not sure if you can read TADS code or not?), the game turns off the library’s normal routine of calling savepoint() after every action if the player starts entering their own commands. So the player can continue entering their own commands, and although the game state is changing as they do so, the last saved state is not changing. The next time the token reader finds an empty command, it calls undo(), voilá, we’re back where the readthrough left off, and it can immediately execute the next readthrough step.

We’ve recently had a discussion about undo capacity for TADS runners. No, it’s not unlimited… but it sounds like Gargoyle and Parchment have recently updated their capacity. Unfortunately I am not sure if this holds for QTads, and I am sorry to report that a game the size of mine (pretty ridiculously large) has only two undos available (in QTads) at the beginning of the game, and towards the end of the game, undo capacity is lost entirely.

3 Likes

Another note!
If you are including some PC deaths in your readthrough, do not add ‘undo’ to the steps list! The player input for finish options is not handled through readMainCommandTokens, but through the processOptions function.
So don’t do this:

>take rat poison
>drink it
>undo
>out

But rather modify processOptions something like this:

modify processOptions(lst) {
 promptLoop:
    for (;;) {
        local resp;
		local rthr = libGlobal.readthruMode;
        if(!rthr || readthru.needsUndo /* || READTHROUGH IS OVER COND*/) finishOptionsLister.showListAll(lst, 0, 0);	
        "<.commandbefore>";
        statusLine.showStatusLine();
        resp = inputManager.getInputLine(nil, nil);
                // don't force the player to type in 'undo'... fill it out as such 
                // if we get a blank input
		if(resp=='' && rthr /* && ///////////some condition that indicates that game end has not yet been reached*/ ) { 
              "<b>&gt; undo</b>\b"; resp = 'undo'; }	
        "<.commandafter>";
        foreach (local cur in lst)        {
            if (cur.responseMatches(resp)) {
                if (cur.doOption()) {
                    continue promptLoop;
                }
                else { return; }
            } }
        gLibMessages.invalidFinishOption(resp);
    }
}
1 Like

Right. Yes, I realise I can do the same thing in Inform, just stop the undo point from updating while the player is in ‘mucking around’ mode.

Yeah, I think everyone has this problem. Big games have few or no UNDOs, eventually. I feel like I/we need to kick and shout about this! I will start shouting again, soon.

-Wade

3 Likes

John, on undo, if you save your game, how big is the savefile ?

my WIP currently generates a 81,330 bytes savefile and I still can do tens of undos, under Qtads.

OTOH, if you remember my post in the iterative saves debate, you will understand my suggestion: instead of a fixed stepFileName, asking the filename (or taking it from the input, as in SAVE “savename.sav”), allowing multiple step files.

I’ll try this extension, for sure.

Thanks for your effort in this extension, whose usefulness to my WIP can be second on to an eventual porting of ProxyActor to adv3…
dott. Piergiorgio.

2 Likes

My save file is 3.6 MB, and that’s near the beginning of the game when there are two undos available… at the end it grows large enough that undo becomes available. I don’t know how large the save is then…

Sorry, P… I’m not sure I remember what you’re talking about. In this case—loading the readthrough steps—, I don’t think it’s very important to do a filename prompt, because the file is something that will only be needed at compile time, and only the author sees it.

If you copied/downloaded the extension code prior to this morning, I’ve already found one typo (this always happens to me when I extract code from my own mess and try to generalize it). There was a place where there is a call to savepointOff which lacked parentheses. That spot needs to be changed to savepointOff(), or else recopy the code from this post (which has been updated).

@Piergiorgio_d_errico Oh, I missed part of your post… you’re saying that the author might create several different playthroughs, and the player can select which one to use? Well certainly, go ahead and modify the module however you like! I can see where a filename prompt would be needful then. I didn’t even think about multiple different readthroughs, but that’s probably because my game can pretty much hit all of the content in one playthrough.

1 Like

Wade, I’d be interested to keep updated on your progress with the Inform version. Is it going to be baked into your game, or will it be a module that other authors can use? Maybe we can sort of collaborate to keep the functionalities of the two versions in sync, for instance if you think of other features to add that aren’t currently in mine, or think that certain behavior should be changed.

1 Like

An important omission in the original post… your game needs to #include dynfunc.t…

1 Like

Please don’t do that. Simply add it as a source in your project makefile (.t3m) :stuck_out_tongue:

-source dynfunc.t
2 Likes

Are TADS authors not supposed to use include statements…? I’m confused by what you mean here.

1 Like

*.t files don’t have multiple-inclusion guards (#pragma once). They are not supposed to be included. Only header files are (.h).

Also, header files never define anything that produces link-time symbols. They only declare things, making them safe to include in different translation units (each generated object file is the result of compiling one translation unit.)

In other words, TADS uses “seperate compilation”. See:

http://tads.org/t3doc/doc/techman/t3inc.htm

If you #include .t files, it will work, but if you end up including the same .t file somewhere else as well, linking will fail, and the error message is not exactly helpful:

error: symbol "CompilerException" (type: object) redefined in object file "obj/main.t3o"

If a user ever ends up doing this by mistake in a large project and is not explicitly aware of the issue, you can imagine the confusion.

If you ever programmed in a language like C or C++, the reason why you shouldn’t #include .t files is basically the same as the reason you shouldn’t #include source files (.c, .cpp) in those languages.

3 Likes

@johnnywz00 Right now it’s baked into the game. I’m not great at making things modular/portable. Also, my game is a bit atypical for Inform. I’m using the Unified Glulx Input extension (UGI) which includes some low level rewriting of the parser. And based on UGI, I have single-keypress choice interludes, so this readthrough mode caters for those as well. I don’t know if UGI even runs in the current version of Inform. I’ve had to stay with version 6M62 because this is year four on this game.

That said, there wasn’t much code to write overall. I just had to get the finicky timing of when it does various things right. I haven’t added the ability to muck around and rewind, yet, but I will.

The behaviour of it overall so far is:

  • If the mode’s on, the command it will enter next appears before the prompt. Press enter to accept it. Or you can type the same command yourself if you feel like it.
  • Entering out of world commands won’t break out of the mode. e.g. You can SAVE, or use an out of world command like GOALS.
  • Otherwise, if you type a different command, you just leave the mode.

-Wade

2 Likes

Thanks for pointing this out, Nikos. The makefile is actually how I included dynfunc in my game, and what I really meant was to include DynamicFunc in the project, not specifically to #include it, although I wasn’t thinking about the hazards of actually using #include. So thanks for the catch.

1 Like

Wow, so you’ve got a four-year game too! Interesting that you’ve got some of that low-level control… one thing that bugs me is that I don’t have a way to paste in the readthrough command to the input line without making a custom interpreter…

1 Like

While I’ve “got” you, is there any possibility of increasing undo capacity for QTads??

1 Like

Hopefully this week.

3 Likes

Wow, that’s fantastic!

1 Like

Update: it seems that release build saves may be more compact than the debug ones? I just used my readthrough mode to autoplay my game to the end (in release build this time), and even after 3200 moves, this save file was only 2.1 MB instead of the earlier 3.6 MB. I also found that after 3200 moves, I still had 2 undos available, which was encouraging. I assume I was playing on a debug build when I actually ran out of undos.

1 Like

John, I fear that there’s an misunderstandment, stemming from your not finding my post, whose was this:

https://intfiction.org/t/iterative-saves/59522/6

As you can see, I use cut’n paste from “service walkthrus”, record/replay &c. for reaching branching points then paste/replay the branch toward the point in the story I’m working (of course, I always kept a backup keyboard, in case the increased wear & tear on poor spacebar reach its predictable outcome…) hence the obvious usefulness of readthru in this debug/testing context
(I’m currently weighting a major split in the narration, where the player can skip half of the “solo” early game, early starting the middle game, with the first of the pair of two NPC, option whose require a careful balancing of the narration, the game being centered on my concept of “treasure hunt of knowledge/lore”, and I should care of the skipped knowledge base, the alternative is easy technically (a barrier) but until now I don’t have figured a solid rationale for the barrier, so I’m currently working on the base of both alternatives)
hence the specific usefulness of the readthru mode in testing & debugging.

Best regards from Italy,
dott. Piergiorgio.

1 Like