Mercurial > hg > early-roguelike
view urogue/options.c @ 296:000b1c5b8d63
UltraRogue: fix inventory collision after save and restore.
Inventory letters are based on "identifiers" stored in objects' o_ident
field. Identifiers are allocated by get_ident(), which keeps a list of
objects that have them, to avoid giving the same identifier to multiple
objects.
The list is not stored in the savefile, so after restore, get_ident()
was not aware of existing identifiers. This resulted in picked-up
objects having the same inventory letters as objects restored from the
file.
The restore code now adds all objects with identifiers to the list.
author | John "Elwin" Edwards |
---|---|
date | Mon, 15 Jan 2018 20:20:35 -0500 |
parents | 9dcf3344d3fd |
children | 0250220d8cdd |
line wrap: on
line source
/* options.c - This file has all the code for the option command UltraRogue: The Ultimate Adventure in the Dungeons of Doom Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong All rights reserved. Based on "Advanced Rogue" Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka All rights reserved. Based on "Rogue: Exploring the Dungeons of Doom" Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman All rights reserved. See the file LICENSE.TXT for full copyright and licensing information. */ #include <string.h> #include <ctype.h> #include "rogue.h" #define NUM_OPTS (sizeof optlist / sizeof (OPTION)) #define EQSTR(a, b, c) (strncmp(a, b, c) == 0) int get_restr(opt_arg *opt, WINDOW *win); int get_score(opt_arg *opt, WINDOW *win); /* description of an option and what to do with it */ static OPTION optlist[] = { {"jump","Show position only at end of run (jump): ", &jump,put_bool,get_bool}, {"inven","Style of inventories (inven): ", &inv_type, put_inv, get_inv}, {"askme","Ask me about unidentified things (askme): ",&askme,put_bool,get_bool}, {"doorstop","Stop running when adjacent (doorstop): ",&doorstop,put_bool,get_bool}, {"name", "Name (name): ", &whoami, put_str, get_restr}, {"fruit", "Fruit (fruit): ", &fruit, put_str, get_str}, {"file", "Save file (file): ", &file_name, put_str, get_restr}, {"score", "Score file (score): ", &score_file, put_str, get_score}, {"class", "Character class (class): ",&char_type, put_abil, get_abil} }; /* option() print and then set options from the terminal */ void option(void) { OPTION *op; int retval; wclear(hw); touchwin(hw); /* Display current values of options */ for (op = optlist; op < &optlist[NUM_OPTS]; op++) { waddstr(hw, op->o_prompt); (*op->o_putfunc)(&op->o_opt, hw); waddch(hw, '\n'); } /* Set values */ wmove(hw, 0, 0); for (op = optlist; op < &optlist[NUM_OPTS]; op++) { waddstr(hw, op->o_prompt); retval = (*op->o_getfunc)(&op->o_opt, hw); if (retval) if (retval == QUIT) break; else if (op > optlist) /* MINUS */ { wmove(hw, (int)(op - optlist) - 1, 0); op -= 2; } else /* trying to back up beyond the top */ { putchar('\007'); wmove(hw, 0, 0); op--; } } /* Switch back to original screen */ mvwaddstr(hw, LINES - 1, 0, spacemsg); wrefresh(hw); wait_for(' '); clearok(cw, TRUE); touchwin(cw); } /* put_bool() put out a boolean */ void put_bool(opt_arg *opt, WINDOW *win) { waddstr(win, *opt->iarg ? "True" : "False"); } /* put_str() put out a string */ void put_str(opt_arg *opt, WINDOW *win) { waddstr(win, opt->str); } /* put_abil() print the character type */ void put_abil(opt_arg *opt, WINDOW *win) { char *abil; switch(*opt->iarg) { case C_FIGHTER: abil = "Fighter"; break; case C_MAGICIAN: abil = "Magic User"; break; case C_CLERIC: abil = "Cleric"; break; case C_THIEF: abil = "Thief"; break; case C_PALADIN: abil = "Paladin"; break; case C_RANGER: abil = "Ranger"; break; case C_ILLUSION: abil = "Illusionist"; break; case C_ASSASIN: abil = "Assasin"; break; case C_NINJA: abil = "Ninja"; break; case C_DRUID: abil = "Druid"; break; default: abil = "(unknown)"; } waddstr(win, abil); } /* get_bool() allow changing a boolean option and print it out */ int get_bool(opt_arg *opt, WINDOW *win) { int oy, ox; int op_bad; op_bad = TRUE; getyx(win, oy, ox); waddstr(win, *opt->iarg ? "True" : "False"); while(op_bad) { wmove(win, oy, ox); wrefresh(win); switch (readcharw(win)) { case 't': case 'T': *opt->iarg = TRUE; op_bad = FALSE; break; case 'f': case 'F': *opt->iarg = FALSE; op_bad = FALSE; break; case '\n': case '\r': op_bad = FALSE; break; case '\033': case '\007': return QUIT; case '-': return MINUS; default: mvwaddstr(win, oy, ox + 10, "(T or F)"); } } wmove(win, oy, ox); wclrtoeol(win); waddstr(win, *opt->iarg ? "True" : "False"); waddch(win, '\n'); return(NORM); } /* get_str() set a string option */ int get_str(opt_arg *opt, WINDOW *win) { return( get_string(opt->str, win) ); } /* get_abil() The ability field is read-only */ int get_abil(opt_arg *opt, WINDOW *win) { int oy, ox, ny, nx; int op_bad; op_bad = TRUE; getyx(win, oy, ox); put_abil(opt, win); getyx(win, ny, nx); while(op_bad) { wmove(win, oy, ox); wrefresh(win); switch(readcharw(win)) { case '\n': case '\r': op_bad = FALSE; break; case '\033': case '\007': return(QUIT); case '-': return(MINUS); default: mvwaddstr(win, ny, nx + 5, "(no change allowed)"); } } wmove(win, ny, nx + 5); wclrtoeol(win); wmove(win, ny, nx); waddch(win, '\n'); return(NORM); } /* parse_opts() parse options from string, usually taken from the environment. the string is a series of comma seperated values, with booleans being stated as "name" (true) or "noname" (false), and strings being "name=....", with the string being defined up to a comma or the end of the entire option string. */ void parse_opts(char *str) { char *sp; const OPTION *op; size_t len; while (*str) { for (sp = str; isalpha(*sp); sp++) continue; len = sp - str; /* Look it up and deal with it */ for (op = optlist; op < &optlist[NUM_OPTS]; op++) { if (EQSTR(str, op->o_name, len)) { if (op->o_putfunc == put_bool) *op->o_opt.iarg = TRUE; else /* string option */ { char *start; char value[80]; /* Skip to start of string value */ for (str = sp + 1; *str == '='; str++) continue; start = (char *) value; /* Skip to end of string value */ for (sp = str + 1; *sp && *sp != ','; sp++) continue; strncpy(start, str, sp - str); /* Some options can't be changed. */ if (use_savedir && (op->o_opt.str == whoami || op->o_opt.str == file_name || op->o_opt.str == score_file)) break; /* Put the value into the option field */ if (op->o_putfunc != put_abil && op->o_putfunc != put_inv) strcpy(op->o_opt.str, value); if (op->o_putfunc == put_inv) { int *opt = op->o_opt.iarg; len = strlen(value); if (isupper(value[0])) value[0] = (char) tolower(value[0]); if (EQSTR(value, "overwrite",len)) *opt = INV_OVER; if (EQSTR(value, "slow", len)) *opt = INV_SLOW; if (EQSTR(value, "clear", len)) *opt = INV_CLEAR; } else if (*op->o_opt.iarg == -1) { int *opt = op->o_opt.iarg; len = strlen(value); if (isupper(value[0])) value[0] = (char) tolower(value[0]); if (EQSTR(value, "fighter", len)) *opt = C_FIGHTER; else if (EQSTR(value, "magic", min(len, 5))) *opt = C_MAGICIAN; else if (EQSTR(value, "illus", min(len, 5))) *opt = C_ILLUSION; else if (EQSTR(value, "cleric", len)) *opt = C_CLERIC; else if (EQSTR(value, "thief", len)) *opt = C_THIEF; else if (EQSTR(value, "paladin", len)) *opt = C_PALADIN; else if (EQSTR(value, "ranger", len)) *opt = C_RANGER; else if (EQSTR(value, "assasin", len)) *opt = C_ASSASIN; else if (EQSTR(value, "druid", len)) *opt = C_DRUID; else if (EQSTR(value, "ninja", len)) *opt = C_NINJA; } } break; } else if (op->o_putfunc == put_bool && EQSTR(str, "no", 2) && EQSTR(str + 2, op->o_name, len - 2)) { *op->o_opt.iarg = FALSE; break; } } /* skip to start of next option name */ while (*sp && !isalpha(*sp)) sp++; str = sp; } } /* put_inv() print the inventory type */ void put_inv(opt_arg *opt, WINDOW *win) { char *style; switch(*opt->iarg) { case INV_OVER: style = "Overwrite"; break; case INV_SLOW: style = "Slow"; break; case INV_CLEAR: style = "Clear Screen"; break; default: style = "(unknown)"; } waddstr(win, style); } /* get_inv() The inventory field. */ int get_inv(opt_arg *opt, WINDOW *win) { int oy, ox, ny, nx; int op_bad; op_bad = TRUE; getyx(win, oy, ox); put_inv(opt, win); getyx(win, ny, nx); while(op_bad) { wmove(win, oy, ox); wrefresh(win); switch(readcharw(win)) { case '\n': case '\r': op_bad = FALSE; break; case '\033': case '\007': return(QUIT); case '-': return(MINUS); case 'O': case 'o': *opt->iarg = INV_OVER; op_bad = FALSE; break; case 'S': case 's': *opt->iarg = INV_SLOW; op_bad = FALSE; break; case 'C': case 'c': *opt->iarg = INV_CLEAR; op_bad = FALSE; break; default: mvwaddstr(win, ny, nx + 5, "(Use: o, s, or c)"); } } wmove(win, oy, ox); wclrtoeol(win); switch(*opt->iarg) { case INV_SLOW: waddstr(win, "Slow\n"); break; case INV_CLEAR: waddstr(win, "Clear Screen\n"); break; case INV_OVER: waddstr(win, "Overwrite\n"); break; default: waddstr(win, "Unknown\n"); break; } return(NORM); } /* * get_restr() * * Gets strings that cannot be changed when use_savedir is set. * get_abil() can't be repurposed to do this without ugliness. * */ int get_restr(opt_arg *opt, WINDOW *win) { int oy, ox, ny, nx; int keep_up; keep_up = TRUE; getyx(win, oy, ox); put_str(opt, win); if (!use_savedir) { wmove(win, oy, ox); return get_str(opt, win); } getyx(win, ny, nx); while(keep_up) { wmove(win, oy, ox); wrefresh(win); switch(readcharw(win)) { case '\n': case '\r': keep_up = FALSE; break; case '\033': case '\007': return(QUIT); case '-': return(MINUS); default: mvwaddstr(win, ny, nx + 5, "(no change allowed)"); } } wmove(win, ny, nx + 5); wclrtoeol(win); wmove(win, ny, nx); waddch(win, '\n'); return(NORM); } int get_score(opt_arg *opt, WINDOW *win) { char old_score_file[2*LINELEN]; int status; if (use_savedir) return get_restr(opt, win); strncpy(old_score_file, opt->str, 2*LINELEN); old_score_file[2*LINELEN - 1] = '\0'; status = get_str(opt, win); if (status == NORM && strcmp(old_score_file, opt->str)) { fclose(fd_score); fd_score = fopen(score_file, "r+"); if (fd_score == NULL) fd_score = fopen(score_file, "a+"); } return status; }