To provide Glulx-specific actions.


§1. Begin Glulx-only matter.

#Ifdef TARGET_GLULX;

§2. GlkList Command. GLKLIST is a testing command best used by those who understand Glulx and its ways: it isn't documented in the I7 manual, because it is pretty inscrutable for "real" users, but it's probably worth keeping just the same.

#Ifdef DEBUG;
[ GlkListSub id val;
    id = glk_window_iterate(0, gg_arguments);
    while (id) {
        print "Window ", id, " (", gg_arguments-->0, "): ";
        val = glk_window_get_type(id);
        switch (val) {
          1: print "pair";
          2: print "blank";
          3: print "textbuffer";
          4: print "textgrid";
          5: print "graphics";
          default: print "unknown";
        }
        val = glk_window_get_parent(id);
        if (val) print ", parent is window ", val;
        else     print ", no parent (root)";
        val = glk_window_get_stream(id);
        print ", stream ", val;
        val = glk_window_get_echo_stream(id);
        if (val) print ", echo stream ", val;
        print "^";
        id = glk_window_iterate(id, gg_arguments);
    }
    id = glk_stream_iterate(0, gg_arguments);
    while (id) {
        print "Stream ", id, " (", gg_arguments-->0, ")^";
        id = glk_stream_iterate(id, gg_arguments);
    }
    id = glk_fileref_iterate(0, gg_arguments);
    while (id) {
        print "Fileref ", id, " (", gg_arguments-->0, ")^";
        id = glk_fileref_iterate(id, gg_arguments);
    }
    if (glk_gestalt(gestalt_Sound, 0)) {
        id = glk_schannel_iterate(0, gg_arguments);
        while (id) {
            print "Soundchannel ", id, " (", gg_arguments-->0, ")^";
            id = glk_schannel_iterate(id, gg_arguments);
        }
    }
];

Verb meta 'glklist'
    *                                           -> GlkList;
#Endif;

§3. Undo. These are really emulations of the Z-machine's conventions on UNDO: Glulx's undo opcodes used different result codes while providing essentially the same functionality, for reasons which are opaque, but no trouble is caused thereby.

[ VM_Undo result_code;
    @restoreundo result_code;
    return (~~result_code);
];

[ VM_Save_Undo result_code;
    @saveundo result_code;
    if (result_code == -1) { GGRecoverObjects(); return 2; }
    return (~~result_code);
];

§4. Quit The Game Rule.

[ QUIT_THE_GAME_R;
    if (actor ~= player) rfalse;
    if ((actor == player) && (untouchable_silence == false))
        QUIT_THE_GAME_RM('A');
    if (YesOrNo()~=0) quit;
];

§5. Restart The Game Rule.

[ RESTART_THE_GAME_R;
    if (actor ~= player) rfalse;
    RESTART_THE_GAME_RM('A');
    if (YesOrNo()~=0) {
        @restart;
        RESTART_THE_GAME_RM('B'); new_line;
    }
];

§6. Restore The Game Rule.

[ RESTORE_THE_GAME_R res fref;
    if (actor ~= player) rfalse;
    fref = glk_fileref_create_by_prompt($01, $02, 0);
    if (fref == 0) jump RFailed;
    gg_savestr = glk_stream_open_file(fref, $02, GG_SAVESTR_ROCK);
    glk_fileref_destroy(fref);
    if (gg_savestr == 0) jump RFailed;
    @restore gg_savestr res;
    glk_stream_close(gg_savestr, 0);
    gg_savestr = 0;
    .RFailed;
    RESTORE_THE_GAME_RM('A'); new_line;
];

§7. Save The Game Rule.

[ SAVE_THE_GAME_R res fref;
    if (actor ~= player) rfalse;
    fref = glk_fileref_create_by_prompt($01, $01, 0);
    if (fref == 0) jump SFailed;
    gg_savestr = glk_stream_open_file(fref, $01, GG_SAVESTR_ROCK);
    glk_fileref_destroy(fref);
    if (gg_savestr == 0) jump SFailed;
    @save gg_savestr res;
    if (res == -1) {
        The player actually just typed "restore". We first have to recover
        all the Glk objects; the values in our global variables are all wrong.
        GGRecoverObjects();
        glk_stream_close(gg_savestr, 0); stream_close
        gg_savestr = 0;
        RESTORE_THE_GAME_RM('B'); new_line;
        rtrue;
    }
    glk_stream_close(gg_savestr, 0); stream_close
    gg_savestr = 0;
    if (res == 0) { SAVE_THE_GAME_RM('B'); new_line; rtrue; }
    .SFailed;
    SAVE_THE_GAME_RM('A'); new_line;
];

§8. Verify The Story File Rule. This is a fossil now, really, but in the days of Infocom, the 110K story file occupying an entire disc was a huge data set: floppy discs were by no means a reliable medium, and cheap hardware often used hit-and-miss components, as on the notorious Commodore 64 disc controller. If somebody experienced an apparent bug in play, it could easily be that he had a corrupt disc or was unable to read data of that density. So the VERIFY command, which took up to ten minutes on some early computers, would chug through the entire story file and compute a checksum, compare it against a known result in the header, and determine that the story file could or could not properly be read. The Z-machine provided this service as an opcode, and so Glulx followed suit.

[ VERIFY_THE_STORY_FILE_R res;
    if (actor ~= player) rfalse;
    @verify res;
    if (res == 0) { VERIFY_THE_STORY_FILE_RM('A'); new_line; rtrue; }
    VERIFY_THE_STORY_FILE_RM('B'); new_line;
];

§9. Switch Transcript On Rule.

[ SWITCH_TRANSCRIPT_ON_R;
    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;
];

§10. Switch Transcript Off Rule.

[ SWITCH_TRANSCRIPT_OFF_R;
    if (actor ~= player) rfalse;
    if (gg_scriptstr == 0) { SWITCH_TRANSCRIPT_OFF_RM('A'); new_line; rtrue; }
    SWITCH_TRANSCRIPT_OFF_RM('B'); new_line;
    glk_stream_close(gg_scriptstr, 0); stream_close
    gg_scriptstr = 0;
];

§11. Announce Story File Version Rule.

[ ANNOUNCE_STORY_FILE_VERSION_R ix;
    if (actor ~= player) rfalse;
    Banner();
    print "Inform 7 v", (PrintI6Text) I7_FULL_VERSION_NUMBER, "^";
    print "Identification number: ";
    for (ix=6: ix <= UUID_ARRAY->0: ix++) print (char) UUID_ARRAY->ix;
    print "^";
    @gestalt 1 0 ix;
    print "Interpreter version ", ix / $10000, ".", (ix & $FF00) / $100,
    ".", ix & $FF, " / ";
    @gestalt 0 0 ix;
    print "VM ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, "^";
    ShowExtensionVersions();
    say__p = 1;
];

§12. Descend To Specific Action Rule. There are 100 or so actions, typically, and this rule is for efficiency's sake: rather than perform 100 or so comparisons to see which routine to call, we indirect through a jump table. The routines called are the -Sub routines: thus, for instance, if action is ##Wait then WaitSub is called. It is essential that this routine not be called for fake actions: in I7 use this is guaranteed, since fake actions are not allowed into the action machinery at all.

Strangely, Glulx's action routines table is numbered in an off-by-one way compared to the Z-machine's: hence the +1.

[ DESCEND_TO_SPECIFIC_ACTION_R;
    indirect(#actions_table-->(action+1));
    rtrue;
];

§13. End Glulx-only matter.

#Endif;