/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "glk/agt/agility.h" #include "glk/agt/interp.h" #include "common/str.h" namespace Glk { namespace AGT { /* ------------------------------------------------------------------- */ /* Description Pointers */ descr_ptr intro_ptr; descr_ptr title_ptr, ins_ptr; /* Only defined if agx_file is true */ descr_ptr *err_ptr; /* [NUM_ERR];*/ descr_ptr *msg_ptr; /* [MAX_MSG];*/ descr_ptr *help_ptr, *room_ptr, *special_ptr; /*[ROOM] */ descr_ptr *noun_ptr, *text_ptr, *turn_ptr, /* [NOUN] */ *push_ptr, *pull_ptr, *play_ptr; descr_ptr *talk_ptr, *ask_ptr, *creat_ptr; /* [CREAT] */ descr_ptr *quest_ptr, *ans_ptr; /* [MAX_QUEST] */ tline *question, *answer; /* [MAX_QUEST] */ /* ------------------------------------------------------------------------ */ /* Dynamically allocated data blocks (which are pointed to from elsewhere) */ char **dict; /* dict[n] points to the nth dictionary word */ long dp; /* Dictionary pointer: number of words in dict */ #define DICT_INIT 12*1024 /* Starting size of dictstr */ #define DICT_GRAN 1024 /* Granularity of dictstr size requests must be at least 4. */ char *dictstr; /* Pointer to memory block containing dict words */ long dictstrptr, dictstrsize; /* dictstrptr points to the first unused byte in dictstr. dictstrsize points to the end of the space currently allocated for dictstr. */ char *static_str; /*Static string space */ long ss_end; /* Pointer to end of used space in above */ long ss_size; /* Current size of static string space */ word *syntbl; /* Synonym list space */ slist synptr; /* Points to end of used space */ long syntbl_size; /* Long so we can catch overflows */ long descr_maxmem; char *mem_descr; /* Copy of descriptor in memory */ /* ------------------------------------------------------------------------ */ /* Data structures used internally by agtread.c */ /*The following are all set to NULL after agtread finishes. */ long *cmd_ptr; /* ME only;Points to cmd start locs in gamefile.*/ long *room_name, *noun_sdesc, *noun_pos, *creat_sdesc; long *t_pictlist, *t_pixlist, *t_songlist, *t_fontlist; /* These are only used by agtout (to allow the AGT reading routines to pass back the count of nouns inside the given object) */ integer *room_inside, *noun_inside, *creat_inside; /* This is used to translate ASCII codes */ uchar fixchar[256]; rbool text_file; /* Set if we are currently opening a binary file. */ #ifdef OPEN_AS_TEXT rbool open_as_binary; /* Open text files as binary, anyhow. */ #endif /* The following are AGT 'purity' flags; they turn off features of */ /* my interpreter that are not fully consistent with the original AGT */ /* and so could break some games. Some of these are trivial improvements; */ /* some are more radical and should be used with caution. Several are */ /* only useful if a game was designed with them in mind. */ /* In all cases, setting the flag to 1 more closely follows the */ /* behavior of the original interpreters */ /* WARNING: Most of these haven't been tested in the non-default state. */ /* Most of these will eventually become variables settable by the user */ /* or from a (possibly game-specific) configuration file */ rbool PURE_INPUT = 1; /* Is the input line bold? */ rbool PURE_TONE = 0; /* Is low level sound enabled? */ /*-------------------------------------------------------------------*/ /* Misc. things to support the tokenizer and the dictionry. */ /*-------------------------------------------------------------------*/ /* The following should not be changed without also changing the wtype enum statement in interp.h to match */ static const char *ext_voc[] = { "the", "my", "a", "an", /* These 4 are ignored in canonical AGT */ "then", ".", ";", "and", "," , "its", "all", "undo", "look", "g", "pick", "go", "exits", "talk", "take", "door", "again", "but", "except", "scene", "everything", "listexit", "listexits", "close", "verb", "noun", "adjective", "prep", "object", "name", "step", " any", "either", "both", "everyone", "everybody", "he", "she", "it", "they", "him", "her", "them", "is", "are", "oops", "was", "were", /* Everything between 'in' and 'about' should be a preposition */ "in", "out", "into", "at", "to", "across", "inside", "with", "near", "for", "of", "behind", "beside", "on", "off", "under", "from", "through", "toward", "towards", "between", "around", "upon", "thru", "by", "over", "up", "down", "about" }; /* 'about' must be the last element of this list */ /*-------------------------------------------------------------------*/ /* Routines to read in and use various auxilary files. */ /* (.TTL, .INS, .VOC, .CFG) */ /*-------------------------------------------------------------------*/ static rbool emptyline(unsigned char *s) /* Check if s consists only of white space and control characters */ { unsigned char *p; for (p = s; *p != 0; p++) if (!rspace(*p) && *p > 26) return 0; return 1; } static void print_title(fc_type fc) { int height; signed char center_mode; /* Center title? */ descr_line *buff; char *s; rbool skip_line; /* Skip first line: it has COLORS */ if (agx_file) buff = read_descr(title_ptr.start, title_ptr.size); else buff = read_ttl(fc); if (buff == NULL) { writeln(""); writeln(""); s = formal_name(fc, fNONE); if (s != NULL) { s[0] = toupper(s[0]); agt_center(1); agt_textcolor(-1); writeln(s); agt_textcolor(-2); agt_center(0); rfree(s); } writeln(""); writeln(""); if (aver < AGX00) writeln("This game was created with Malmberg and Welch's Adventure " "Game Toolkit; it is being executed by"); else writeln("This game is being executed by "); writeln(""); height = 0; } else { if (buff[0] != NULL && strncasecmp(buff[0], "COLORS", 6) == 0) { /* Do screen colors */ skip_line = 1; } else skip_line = 0; /* Compute height and count the number of non-empty lines starting with spaces. We use height as a loop variable and center_mode to store the count temporarily. */ center_mode = 0; for (height = skip_line; buff[height] != NULL; height++) if (!emptyline((uchar *)buff[height])) { if (rspace(buff[height][0])) center_mode++; else center_mode--; } if (box_title || aver == AGTCOS) center_mode = TB_CENTER; else /* includes aver==AGT135 */ if (center_mode <= 0) center_mode = TB_CENTER; else center_mode = TB_NOCENT; if (!bold_mode) agt_textcolor(-1); agt_clrscr(); textbox(buff + skip_line, height - skip_line, center_mode | (bold_mode ? 0 : TB_BOLD) | TB_TTL | (box_title ? TB_BORDER : 0)); if (!bold_mode) agt_textcolor(-2); /* Bold off */ } /* End printing of title proper */ if (agx_file) free_descr(buff); else free_ttl(buff); agt_textcolor(7); agt_center(1); if (buff != NULL) { if (aver < AGX00 && height <= screen_height - 6) writeln("[Created with Malmberg and Welch's Adventure Game Toolkit]"); if (height <= screen_height - 9) writeln(""); if (height <= screen_height - 5) writeln("This game is being executed by"); } agt_textcolor(-1); s = (char *)rmalloc(80); if (height <= screen_height - 5) sprintf(s, "AGiliTy: " "The (Mostly) Universal AGT Interpreter %s", version_str); else sprintf(s, "Being run by AGiliTy %s, " "Copyright (C) 1996-99,2001 Robert Masenten", version_str); writeln(s); rfree(s); agt_textcolor(-2); if (height <= screen_height - 5) writeln("Copyright (C) 1996-99,2001 by Robert Masenten"); if (height <= screen_height - 3) writeln(portstr); if (height <= screen_height - 10) writeln(""); agt_center(0); } /* .INS reading routines -------------------------------------- */ void print_instructions(fc_type fc) { char *buffer; uchar *s; writeln("INSTRUCTIONS:"); if (open_ins_file(fc, 1)) { /* Instruction file exists */ while (NULL != (buffer = read_ins_line())) { for (s = (uchar *)buffer; *s != 0; s++) *s = trans_ascii[*s]; writeln(buffer); } } writeln(""); } /* Routines to build the verb menu from the .VOC information */ static void build_verbmenu(void) { int i, n; char *p, *d; verbmenu = (menuentry *)rmalloc(vm_size * sizeof(menuentry)); vm_width = 0; for (i = 0; i < vm_size; i++) { p = verbmenu[i]; d = dict[verbinfo[i].verb]; n = 0; for (; n < MENU_WIDTH && *d != 0; p++, d++, n++) *p = *d; if (verbinfo[i].prep != 0 && n + 1 < MENU_WIDTH) { *p++ = ' '; d = dict[verbinfo[i].prep]; *p++ = toupper(*d++); for (; n < MENU_WIDTH && *d != 0; p++, d++, n++) *p = *d; } verbmenu[i][0] = toupper(verbmenu[i][0]); *p = 0; if (n > vm_width) vm_width = n; } } /* .CFG reading routines -------------------------------------------- */ #define opt(s) (strcasecmp(optstr[0],s)==0) /* These are the interpreter specific options; this is called from cfg_option in agtdata.c. */ void agil_option(int optnum, char *optstr[], rbool setflag, rbool lastpass) { if (opt("ibm_char")) fix_ascii_flag = !setflag; else if (!lastpass) return; /* On the first pass through the game specific file, we ignore all but the above options */ else if (opt("tone")) PURE_TONE = setflag; else if (opt("input_bold")) PURE_INPUT = setflag; else if (opt("force_load")) FORCE_VERSION = setflag; else if (!agt_option(optnum, optstr, setflag)) /* Platform specific options */ rprintf("Invalid option %s\n", optstr[0]); } /*-------------------------------------------------------------------*/ /* Tokeniser: Split input into words and look them up in dictionary */ /*-------------------------------------------------------------------*/ static rbool noise_word(word w) { if (w == ext_code[wthe] || w == ext_code[wa] || w == ext_code[wan]) return 1; if (w == ext_code[wmy]) return 1; if (aver >= AGT18 && aver <= AGT18MAX && w == ext_code[wis]) return 1; return 0; } static rbool check_dot(char *prevtext, int prevcnt, char *lookahead) /* This routine is devoted to trying to figure out whether we should treat '.' as punctuation or as a letter. */ /* It returns true if '.' should be treated as punctuation. */ /* prevtext=the current word, as far as it has been parsed. prevcnt=the number of letters in prevtext [which is *not* 0 terminated] */ /* lookahead=the rest of the current input line *after* the period. */ { int i, endword, restcnt; if (!PURE_DOT) return 1; /* No words with periods in them, so it must be punctuation. */ /* We just start scanning the dictionary to see if any of them are possible matches, looking ahead as neccessary. */ /* Find the next unambiguous word end. This ignores possible word ends caused by periods. */ for (endword = 0; lookahead[endword] != 0; endword++) if (isspace(lookahead[endword]) || lookahead[endword] == ',' || lookahead[endword] == ';') break; for (i = 0; i < dp; i++) { if (i == ext_code[wp]) continue; /* Ignore matches with the word ".". */ /* If it doesn't contain a '.' at the right location, there is no point in continuing. */ restcnt = strlen(dict[i]); if (restcnt <= prevcnt || dict[i][prevcnt] != '.') continue; /* Now make sure the previous characters are correct */ if (strncasecmp(prevtext, dict[i], prevcnt) != 0) continue; /* Finally, compare the trailing text. This is complicated by the fact that the trailing text could itself contain ambiguous '.'s */ restcnt -= prevcnt + 1; /* Number of characters in dict entry after '.' */ if (restcnt > endword) continue; /* Dictionary entry is longer than following text */ /* Check to see if the dictionary entry can be found in the lookahead buffer */ if (strncasecmp(lookahead, dict[i] + prevcnt + 1, restcnt) != 0) continue; if (restcnt == endword) return 0; /* We have a match */ /* At this point, we know that restcnt 0) { /* End of word: add it to input */ in_text[ip_][j] = 0; input[ip_] = search_dict(in_text[ip_]); if (input[ip_] == -1) input[ip_] = 0; else if (input[ip_] == 0) input[ip_] = ext_code[w_any]; /* _Real_ 'ANY' */ if (!noise_word(input[ip_])) ip_ += 1; /* i.e. if not one of the four ignored words, advance */ j = 0; } /* If j=0 and not punct, then no new word; just skip the whitespace */ if (punctuation) { in_text[ip_][0] = buff[k]; in_text[ip_][1] = 0; input[ip_] = search_dict(in_text[ip_]); if (input[ip_] == -1) input[ip_] = 0; j = 0; ip_++; } if (ip_ >= MAXINPUT - 1) { writeln("Too many words in input; ignoring rest of line."); break; } if (buff[k] == 0) break; } input[ip_] = -1; in_text[ip_][0] = 0; } /*-------------------------------------------------------------------*/ /* Main game loop: Get player input and call the parser. */ /*-------------------------------------------------------------------*/ static void game_end(void) { rbool done_flag; char *s; if (winflag || deadflag) { writeln(""); writeln(""); agt_center(1); if (winflag) gen_sysmsg(148, "***** $You$ have won! *****", MSG_MAIN, NULL); if (deadflag) gen_sysmsg(147, "***** $You$ have died! *****", MSG_MAIN, NULL); writeln(""); writeln(""); agt_center(0); } if (deadflag && !endflag) { if (curr_lives > 1) { /* Resurrection code */ if (curr_lives == max_lives) gen_sysmsg(151, "Hmmm.... so $you$'ve gotten $your$self killed. " "Would you like me to try a resurrection?", MSG_MAIN, NULL); else gen_sysmsg(152, " $You$'ve died *again*. " "Would you like me to try another resurrection?", MSG_MAIN, NULL); if (yesno("? ")) { /* Now do resurrection */ curr_lives--; quitflag = deadflag = 0; gen_sysmsg(154, "$You$ emerge coughing from a cloud of dark green smoke.", MSG_MAIN, NULL); writeln(""); loc = resurrect_room - first_room; newlife_flag = 1; set_statline(); do_look = do_autoverb = 1; newroom(); return; } else writeln("As you wish..."); } else if (max_lives > 1) gen_sysmsg(153, "$You$'ve used up all of $your$ lives.", MSG_MAIN, NULL); } writeln(""); print_score(); writeln(""); done_flag = quitflag; /* If player has QUIT, don't ask again */ while (!done_flag && !quitflag) { writestr("Would you like to "); if (restart_state != NULL) writestr("restart, "); writestr("restore"); if (undo_state != NULL && can_undo) writestr(", undo,"); else if (restart_state != NULL) writestr(","); writestr(" or quit? "); s = agt_readline(5); if (strncasecmp(s, "RESTART", 7) == 0) if (restart_state != NULL) { restart_game(); done_flag = 1; } else writeln("Sorry, I'm unable to do that because of limited memory."); else if (strncasecmp(s, "RESTORE", 7) == 0) if (loadgame()) { done_flag = 1; } else writeln("(RESTORE failed)"); else if (strncasecmp(s, "UNDO", 4) == 0) if (can_undo && undo_state != NULL) { putstate(undo_state); done_flag = 1; } else writeln("Insufficiant memory to support UNDO"); else if (toupper(s[0]) == 'Q') { quitflag = 1; done_flag = 1; } } set_statline(); } static void parse_loop(void) /* This exists to deal with THEN lists; parse() handles the indiviudual commands */ { for (ip = 0; ip >= 0 && ip < MAXINPUT && input[ip] != -1;) { if (!parse() || quitflag || winflag || deadflag || endflag) break; if (doing_restore) break; if (ip >= 0 && ip < MAXINPUT && input[ip] != -1) writeln(""); /* Insert blank lines between commands when dealing with THEN lists */ } } static long rm_start_size; static char memstr[100]; static void mainloop(void) { char *s; doing_restore = 0; while (!quitflag) { if (DEBUG_MEM) { sprintf(memstr, "A:%ld F:%ld Delta:%ld Size:%ld+%ld=%ld (%ld left)\n", ralloc_cnt, rfree_cnt, ralloc_cnt - rfree_cnt, rm_start_size, rm_size - rm_start_size, rm_size, rm_freesize); writeln(memstr); } rm_size = 0; /* Reset it to zero */ rm_freesize = get_rm_freesize(); if (!menu_mode) { prompt_out(1); s = agt_readline(0); agt_newline(); if (!doing_restore) tokenise(s); /* Tokenizes into input */ rfree(s); if (!doing_restore) parse_loop(); } else menu_cmd(); if (doing_restore) { if (doing_restore == 1) loadgame(); else if (doing_restore == 2) restart_game(); else if (doing_restore == 3 || doing_restore == 4) return; /* Quit or New game requested */ doing_restore = 0; } if (winflag || deadflag || endflag || quitflag) game_end(); } } /*-------------------------------------------------------------------*/ /* Start up and shut down: Routines to initialise the game state and */ /* clean up after the game ends. */ /*-------------------------------------------------------------------*/ static int init(void) { int i, can_save; uchar *tmp1, *tmp2; init_vals(); init_creat_fix(); if (!agx_file) dict[0][0] = 0; /* Turn "ANY" into "" */ l_stat[0] = r_stat[0] = 0; /* Clear the status line */ /* lactor=lobj=lnoun=NULL;*/ tscore = old_score = objscore = 0; turncnt = 0; curr_time = startup_time; loc = start_room - first_room; cmd_saveable = 0; first_visit_flag = newlife_flag = room_firstdesc = 1; curr_lives = max_lives; /* Note: flag[0] is the debugging flag and is set elsewhere */ if (FLAG_NUM < 0) FLAG_NUM = 0; dbgflagptr = flag = (rbool *)rrealloc(flag, sizeof(rbool) * (FLAG_NUM + 1)); for (i = 1; i <= FLAG_NUM; i++) flag[i] = 0; dbgcntptr = agt_counter = (short *)rmalloc(sizeof(short) * (CNT_NUM + 1)); for (i = 0; i <= CNT_NUM; i++) { agt_counter[i] = -1; } dbgvarptr = agt_var = (long *)rmalloc(sizeof(*agt_var) * (VAR_NUM + 1)); for (i = 0; i <= VAR_NUM; i++) agt_var[i] = 0; for (i = 0; i <= maxnoun - first_noun; i++) { if (noun[i].position == NULL || noun[i].position[0] == '\0') noun[i].pos_prep = 0; else noun[i].pos_prep = -1; noun[i].pos_name = 0; noun[i].initpos = noun[i].position; } nomatch_aware = 0; /* By default, not aware. */ smart_look = 1; /* By default, LOOK --> EXAMINE */ for (i = 0; i < last_cmd; i++) { if (command[i].nouncmd == -1 || command[i].objcmd == -1 || command[i].noun_adj == -1 || command[i].obj_adj == -1 || command[i].prep == -1) nomatch_aware = 1; if (command[i].verbcmd == ext_code[wlook] && (command[i].nouncmd > 0 || command[i].noun_adj > 0 || command[i].objcmd > 0 || command[i].obj_adj > 0 || command[i].prep > 0)) smart_look = 0; } pictable = (integer *)rmalloc(sizeof(int) * maxpict); for (i = 0; i < maxpict; i++) pictable[i] = i; init_state_sys(); /* Initialize the system for saving and restoring game states */ tmp1 = (uchar *)rmalloc(MEM_MARGIN); /* Preserve some work space */ tmp2 = getstate(NULL); /* Make sure we have space to save */ if (tmp2 == NULL) can_save = 0; else can_save = 1; if (tmp2 != NULL) undo_state = getstate(NULL); else undo_state = NULL; if (undo_state != NULL) restart_state = getstate(NULL); else restart_state = NULL; rfree(tmp1); rfree(tmp2); rm_start_size = get_rm_size(); rm_freesize = get_rm_freesize(); return can_save; } static void ext_dict(void) /* Enter the vocabulary extensions into the dictionary */ { wtype i; for (i = wthe; i <= wabout; i = (wtype)((int)i + 1)) ext_code[i] = add_dict(ext_voc[i]); } static void fix_dummy(void) { int i; /* At this point, all occurances in the game file of the dictionary words have been converted to dictionary indices, and so as long as we don't change the dictionary index values, we can change the contents without interfering with the metacommand scanner (since it compares dictionary indices, not actual strings) */ if (!PURE_DUMMY) { for (i = 0; i < DUMB_VERB; i++) dict[ syntbl[auxsyn[i + BASE_VERB]] ][5] = ' '; /* Convert underscores into spaces: i.e. 'dummy_verb5' -> 'dummy verb5' */ dict[ syntbl[auxsyn[21]] ][6] = ' '; /* change_locations */ dict[ syntbl[auxsyn[55]] ][5] = ' '; /* magic_word */ } if (!PURE_SUBNAME) /* Replace the 'e' by a space */ for (i = 0; i < MAX_SUB; i++) sprintf(dict[sub_name[i]], "subroutin %d", i + 1); /* This must be no longer than 25 characters with the terminating null */ /* Now set PURE_DOT based on whether any dictionary word contains a period. */ if (aver >= AGT18 && aver <= AGT18MAX) PURE_DOT = 0; else { PURE_DOT = FORCE_PURE_DOT; for (i = 0; i < dp && !PURE_DOT; i++) if (strchr(dict[i], '.') != NULL && /* i.e. dict[i] contains period */ i != ext_code[wp]) /* The period itself _is_ a dictionary word: avoid this false match */ PURE_DOT = 1; } } /* char *v */ static void print_license(void) { writeln("AGiliTy"); writestr("The (Mostly) Universal AGT Interpreter, "); writeln(version_str); writeln(" Copyright (C) 1996-1999,2001 by Robert Masenten"); writestr("["); writestr(portstr); writeln("]"); writeln("-----------------------------------------------------------"); writeln(""); writeln(" This is an interpreter for game files created with Malmberg and " "Welch's _Adventure Game Toolkit_. AGiliTy is universal in the " "sense that it understands and interprets most of the many versions " "of the AGT game file format."); writeln(" It is *not* a port of the original interpreters but rather a " "completely new interpreter built around the game file format; " "while it follows the original interpreters on most things, there " "are some differences which are described in the file " "'readme.agility' which should have come with this program."); writeln(""); writeln(" This software is copyright 1996-1999,2001 by Robert Masenten "); writeln(" This program is free software; you can redistribute it and/or " "modify it under the terms of version 2 of the GNU General " "Public License as published by the Free Software Foundation."); writeln(" This program is distributed in the hope that it will be useful, " "but WITHOUT ANY WARRANTY; without even the implied warranty of " "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " "GNU General Public License for more details."); writeln(" You should have received a copy of the GNU General Public " "License along with this program; if not, write to the Free " "Software Foundation, Inc., 51 Franklin Street, Fifth Floor, " "Boston, MA 02110-1301 USA"); writeln(""); writeln(" Send comments and bug reports to Robert Masenten at:"); writeln(" rcm-math@pacbell.net"); writeln(""); writeln("ACKNOWLEDGMENTS"); writeln("Thanks to Jay Goemmer, who has sent me pages and pages of " "comments and bug reports; " "David Kinder, responsible for the Amiga and Windows ports and a " "source of much valuble feedback; " "David Youd, who has uncovered many subtle and complex bugs in " "both AGiliTy and Magx; " "Mitch Mlinar, who has contributed several patches and other " "suggestions; " "all of those who have sent me suggestions and bug reports, " "including " "Audrey DeLisle (responsible for the red smoke), David Doherty," "Al Golden, " "John Hartnup, Walter Isaac, Sami Kivela, Alexander Lehmann, " "Grant E. Metcalf, " "Paul Mikell, Adam Myrow, Olav Nielsen, " "D.J. Picton, Kevin Soucy, Ben Straub, \"Grand Moff Tarkin\", " "Adam Thornton, " "Mark Tilford, David Turpin, and Gil Williamson; " "Volker Blasius," " original maintainer of the Interactive Fiction Archive; " "Robert Pelak, who suggested the name \"AGiliTy\"; " "and to everyone on Rec.arts.int-fiction who suggested names for " "my interpreter."); writeln(""); writeln("SPECIAL VERBS RECOGNIZED"); writeln("These are all of the special verbs recognized by the interpreter:"); writeln(" SCORE Print out your score."); writeln(" NOTIFY Turn score notification on and off."); writeln(" INSTRUCTIONS or INS Display the instructions for the game."); writeln(" INTRODUCTION or INTRO Repeat the introduction of the game."); writeln(" VIEW Views an illustration. (Not supported on all " "platforms.)"); writeln(" BRIEF Don't print room descriptions for rooms you've seen."); writeln(" VERBOSE Print room descriptions even for rooms you've already " "seen."); writeln(" LIST EXITS List the exits from a room."); writeln(" LISTEXIT ON/OFF Turn on/off automatic listing of exits."); writeln(" SCRIPT Start sending a transcript to a file."); writeln(" UNSCRIPT Stop creating a transcript."); writeln(" SOUND ON, OFF Turn sound on/off."); writeln(" LOG Start sending all of your commands to a file."); writeln(" REPLAY Replay your commands from a file, " "executing one every seconds."); writeln(" REPLAY FAST Replay your commands from a file without waiting " "for you to read the scrolling text."); writeln(" REPLAY STEP Replay your commands from a file, " "one for every keypress."); writeln(" AGILDEBUG Access debugging commands."); writeln(" MENU Toggle menu mode on or off."); writeln(" OOPS Correct a word you just mistyped; must be the first " "command on a line."); writeln(" UNDO Undo your last move; must be the first command on a line."); writeln(" SAVE Save the game."); writeln(" RESTORE Restore the game."); writeln(" RESTART Restart the game."); writeln(" QUIT Quit."); writeln(""); } /* This is a hack to get rid of the "What Now?" prompt. */ static void fix_prompt(void) { descr_line *d; if (err_ptr == NULL) return; d = read_descr(err_ptr[0].start, err_ptr[0].size); if (d == NULL) return; if (strncasecmp(d[0], "What Now?", 9) == 0) err_ptr[0].size = err_ptr[0].start = 0; free_descr(d); } void close_game(void); /* Called by setup_game, and so needs to be defined here. */ static fc_type setup_game(fc_type fc) /* game_name is the common filename of the AGT game files */ { int can_save; char choice; rbool have_ins; bold_mode = 0; rm_acct = 1; rm_trap = 1; rm_size = ralloc_cnt = rfree_cnt = 0; mars_fix = 0; no_auxsyn = 0; debug_disambig = 0; debug_any = 1; dbg_nomsg = 1; /* Supress output of MSG arguments to metacommands */ textbold = 0; debug_mode = 0; aver = 0; verboseflag = 1; notify_flag = 0; logflag = 0; menu_mode = 0; fast_replay = 0; stable_random = BATCH_MODE || make_test; if (make_test) BATCH_MODE = 0; hold_fc = fc; set_default_filenames(fc); init_stack(); read_config(agt_globalfile(0), 1); /* Global configuration file */ /* Now that we *have* PATH information, go looking for the games */ /* At the very least, it creates an rmalloc'd copy of game_name */ read_config(openfile(fc, fCFG, NULL, 0), 0); text_file = 0; /* First pass through game specific config file */ build_trans_ascii(); #ifdef PROFILE resetwatch(); #endif writeln("Loading game..."); if (!read_agx(fc, 0) && !readagt(fc, 0)) fatal("Unable to load game."); #ifdef PROFILE writeln(stopwatch()); agt_waitkey(); #endif if (have_opt) menu_mode = opt_data[5]; /* See agtread.c for discussion of OPT file format */ text_file = 1; read_config(openfile(fc, fCFG, NULL, 0), 1); /*Game specific config file*/ text_file = 0; if (min_ver > AGIL_VERID) { if (FORCE_VERSION) agtwarn("This game requires a later version of AGiliTy.", 0); else fatal("This game requires a later version of AGiliTy."); } sort_cmd(); ext_dict(); build_verbmenu(); fix_dummy(); /* Prevent player from calling dummy verbs or subroutines by typing 'Subroutine n' on the command line */ can_save = init(); if (!agx_file) open_descr(fc); fix_prompt(); /* Kill off 'What Now?' prompt. */ if (BATCH_MODE || make_test) set_test_mode(fc); start_interface(fc); fontcmd(2, 0); /* Set initial font */ if (intro_first && intro_ptr.size > 0) { agt_clrscr(); print_descr(intro_ptr, 1); wait_return(); } if (aver >= AGTME10) pictcmd(3, 0); /* Show title image, if there is one */ print_title(fc); have_ins = open_ins_file(fc, 0); do { if (have_ins) writestr("Choose nstructions, GiliTy Information, " "or to start the game"); else writestr("Choose GiliTy Information or to start the game"); choice = tolower(agt_getchar()); /* Wait for keypress */ agt_clrscr(); if (have_ins && choice == 'i') print_instructions(fc); else if (choice == 'a') print_license(); } while ((choice == 'i' && have_ins) || choice == 'a'); close_ins_file(); if (!intro_first && intro_ptr.size > 0) { print_descr(intro_ptr, 1); wait_return(); agt_clrscr(); } if (maxroom < first_room) { close_game(); error("Invalid first room"); } set_statline(); if (can_save == 0) { writeln("[Insufficiant memory to support SAVE, RESTORE, or UNDO]"); } else if (undo_state == NULL) writeln("[Insufficiant memory to support UNDO]"); do_look = do_autoverb = 1; newroom(); rm_acct = 1; /* Turn on memory allocation accounting */ return fc; } /* We need to import save_lnoun from exec.c so that we can free it. */ extern parse_rec *save_lnoun; void close_game(void) { if (agx_file) agx_close_descr(); else close_descr(); fontcmd(1, -1); /* Restore original font */ musiccmd(7, -1); /* Clean up */ close_interface(); /* Now free everything in sight; this _shouldn't_ be necessary, but why take chances? */ free_all_agtread(); rfree(restart_state); rfree(undo_state); rfree(pictable); rfree(save_lnoun); rfree(verbptr); rfree(verbend); rfree(agt_counter); rfree(agt_var); free_creat_fix(); flag = (rbool *)rrealloc(flag, sizeof(rbool)); /* Preserve the debugging flag */ if (DEBUG_MEM) debug("\n\nAlloc:%ld Freed:%ld Difference:%ld\n", ralloc_cnt, rfree_cnt, ralloc_cnt - rfree_cnt); } void run_game(fc_type fc) { doing_restore = 0; rm_acct = 1; rm_trap = 1; rm_size = ralloc_cnt = rfree_cnt = 0; read_config(agt_globalfile(0), 1); /* Global configuration file: get PATH information*/ fix_file_context(fc, fDA1); do { if (doing_restore == 3) { release_file_context(&fc); fc = setup_game(new_game()); } else setup_game(fc); doing_restore = 0; mainloop(); close_game(); } while (doing_restore == 3); release_file_context(&fc); } } // End of namespace AGT } // End of namespace Glk