/* File: files.c */ /* * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. Other copyrights may also apply. */ #include "angband.h" /* * You may or may not want to use the following "#undef". */ /* #undef _POSIX_SAVED_IDS */ /* * Hack -- drop permissions */ void safe_setuid_drop(void) { #ifdef SET_UID # ifdef SAFE_SETUID # ifdef SAFE_SETUID_POSIX if (setuid(getuid()) != 0) { quit("setuid(): cannot set permissions correctly!"); } if (setgid(getgid()) != 0) { quit("setgid(): cannot set permissions correctly!"); } # else if (setreuid(geteuid(), getuid()) != 0) { quit("setreuid(): cannot set permissions correctly!"); } if (setregid(getegid(), getgid()) != 0) { quit("setregid(): cannot set permissions correctly!"); } # endif # endif #endif } /* * Hack -- grab permissions */ void safe_setuid_grab(void) { #ifdef SET_UID # ifdef SAFE_SETUID # ifdef SAFE_SETUID_POSIX if (setuid(player_euid) != 0) { quit("setuid(): cannot set permissions correctly!"); } if (setgid(player_egid) != 0) { quit("setgid(): cannot set permissions correctly!"); } # else if (setreuid(geteuid(), getuid()) != 0) { quit("setreuid(): cannot set permissions correctly!"); } if (setregid(getegid(), getgid()) != 0) { quit("setregid(): cannot set permissions correctly!"); } # endif # endif #endif } #if 0 /* * Use this (perhaps) for Angband 2.8.4 * * Extract "tokens" from a buffer * * This function uses "whitespace" as delimiters, and treats any amount of * whitespace as a single delimiter. We will never return any empty tokens. * When given an empty buffer, or a buffer containing only "whitespace", we * will return no tokens. We will never extract more than "num" tokens. * * By running a token through the "text_to_ascii()" function, you can allow * that token to include (encoded) whitespace, using "\s" to encode spaces. * * We save pointers to the tokens in "tokens", and return the number found. */ static s16b tokenize_whitespace(char *buf, s16b num, char **tokens) { int k = 0; char *s = buf; /* Process */ while (k < num) { char *t; /* Skip leading whitespace */ for ( ; *s && isspace(*s); ++s) /* loop */; /* All done */ if (!*s) break; /* Find next whitespace, if any */ for (t = s; *t && !isspace(*t); ++t) /* loop */; /* Nuke and advance (if necessary) */ if (*t) *t++ = '\0'; /* Save the token */ tokens[k++] = s; /* Advance */ s = t; } /* Count */ return (k); } #endif /* * Extract the first few "tokens" from a buffer * * This function uses "colon" and "slash" as the delimeter characters. * * We never extract more than "num" tokens. The "last" token may include * "delimeter" characters, allowing the buffer to include a "string" token. * * We save pointers to the tokens in "tokens", and return the number found. * * Hack -- Attempt to handle the 'c' character formalism * * Hack -- An empty buffer, or a final delimeter, yields an "empty" token. * * Hack -- We will always extract at least one token */ s16b tokenize(char *buf, s16b num, char **tokens) { int i = 0; char *s = buf; /* Process */ while (i < num - 1) { char *t; /* Scan the string */ for (t = s; *t; t++) { /* Found a delimiter */ if ((*t == ':') || (*t == '/')) break; /* Handle single quotes */ if (*t == '\'') { /* Advance */ t++; /* Handle backslash */ if (*t == '\\') t++; /* Require a character */ if (!*t) break; /* Advance */ t++; /* Hack -- Require a close quote */ if (*t != '\'') *t = '\''; } /* Handle back-slash */ if (*t == '\\') t++; } /* Nothing left */ if (!*t) break; /* Nuke and advance */ *t++ = '\0'; /* Save the token */ tokens[i++] = s; /* Advance */ s = t; } /* Save the token */ tokens[i++] = s; /* Number found */ return (i); } /* * Parse a sub-file of the "extra info" (format shown below) * * Each "action" line has an "action symbol" in the first column, * followed by a colon, followed by some command specific info, * usually in the form of "tokens" separated by colons or slashes. * * Blank lines, lines starting with white space, and lines starting * with pound signs ("#") are ignored (as comments). * * Note the use of "tokenize()" to allow the use of both colons and * slashes as delimeters, while still allowing final tokens which * may contain any characters including "delimiters". * * Note the use of "strtol()" to allow all "integers" to be encoded * in decimal, hexidecimal, or octal form. * * Note that "monster zero" is used for the "player" attr/char, "object * zero" will be used for the "stack" attr/char, and "feature zero" is * used for the "nothing" attr/char. * * Specify the attr/char values for "monsters" by race index. * R::/ * * Specify the attr/char values for "objects" by kind index. * K::/ * * Specify the attr/char values for "features" by feature index. * F::/ * * Specify the attr/char values for "special" things. * S::/ * * Specify the attribute values for inventory "objects" by kind tval. * E:: * * Define a macro action, given an encoded macro action. * A: * * Create a macro, given an encoded macro trigger. * P: * * Create a keymap, given an encoded keymap trigger. * C:: * * Turn an option off, given its name. * X: * * Turn an option on, given its name. * Y: * * Turn a window flag on or off, given a window, flag, and value. * W::: * * Specify visual information, given an index, and some data * V::::: */ errr process_pref_file_aux(char *buf) { int i, j, n1, n2; char *zz[16]; /* Skip "empty" lines */ if (!buf[0]) return (0); /* Skip "blank" lines */ if (isspace(buf[0])) return (0); /* Skip comments */ if (buf[0] == '#') return (0); /* Paranoia */ /* if (strlen(buf) >= 1024) return (1); */ /* Require "?:*" format */ if (buf[1] != ':') return (1); /* Process "R::/" -- attr/char for monster races */ if (buf[0] == 'R') { if (tokenize(buf+2, 3, zz) == 3) { monster_race *r_ptr; i = (huge)strtol(zz[0], NULL, 0); n1 = strtol(zz[1], NULL, 0); n2 = strtol(zz[2], NULL, 0); if (i >= MAX_R_IDX) return (1); r_ptr = &r_info[i]; if (n1) r_ptr->x_attr = n1; if (n2) r_ptr->x_char = n2; return (0); } } /* Process "K::/" -- attr/char for object kinds */ else if (buf[0] == 'K') { if (tokenize(buf+2, 3, zz) == 3) { object_kind *k_ptr; i = (huge)strtol(zz[0], NULL, 0); n1 = strtol(zz[1], NULL, 0); n2 = strtol(zz[2], NULL, 0); if (i >= MAX_K_IDX) return (1); k_ptr = &k_info[i]; if (n1) k_ptr->x_attr = n1; if (n2) k_ptr->x_char = n2; return (0); } } /* Process "F::/" -- attr/char for terrain features */ else if (buf[0] == 'F') { if (tokenize(buf+2, 3, zz) == 3) { feature_type *f_ptr; i = (huge)strtol(zz[0], NULL, 0); n1 = strtol(zz[1], NULL, 0); n2 = strtol(zz[2], NULL, 0); if (i >= MAX_F_IDX) return (1); f_ptr = &f_info[i]; if (n1) f_ptr->x_attr = n1; if (n2) f_ptr->x_char = n2; return (0); } } /* Process "S::/" -- attr/char for special things */ else if (buf[0] == 'S') { if (tokenize(buf+2, 3, zz) == 3) { j = (byte)strtol(zz[0], NULL, 0); n1 = strtol(zz[1], NULL, 0); n2 = strtol(zz[2], NULL, 0); misc_to_attr[j] = n1; misc_to_char[j] = n2; return (0); } } /* Process "E::" -- attribute for inventory objects */ else if (buf[0] == 'E') { if (tokenize(buf+2, 2, zz) == 2) { j = (byte)strtol(zz[0], NULL, 0) % 128; n1 = strtol(zz[1], NULL, 0); if (n1) tval_to_attr[j] = n1; return (0); } } /* Process "A:" -- save an "action" for later */ else if (buf[0] == 'A') { text_to_ascii(macro_buffer, buf+2); return (0); } /* Process "P:" -- create macro */ else if (buf[0] == 'P') { char tmp[1024]; text_to_ascii(tmp, buf+2); macro_add(tmp, macro_buffer); return (0); } /* Process "C::" -- create keymap */ else if (buf[0] == 'C') { int mode; char tmp[1024]; if (tokenize(buf+2, 2, zz) != 2) return (1); mode = strtol(zz[0], NULL, 0); if ((mode < 0) || (mode >= KEYMAP_MODES)) return (1); text_to_ascii(tmp, zz[1]); if (!tmp[0] || tmp[1]) return (1); i = (byte)(tmp[0]); string_free(keymap_act[mode][i]); keymap_act[mode][i] = string_make(macro_buffer); return (0); } /* Process "V:::::" -- visual info */ else if (buf[0] == 'V') { if (tokenize(buf+2, 5, zz) == 5) { i = (byte)strtol(zz[0], NULL, 0); angband_color_table[i][0] = (byte)strtol(zz[1], NULL, 0); angband_color_table[i][1] = (byte)strtol(zz[2], NULL, 0); angband_color_table[i][2] = (byte)strtol(zz[3], NULL, 0); angband_color_table[i][3] = (byte)strtol(zz[4], NULL, 0); return (0); } } /* Process "X:" -- turn option off */ else if (buf[0] == 'X') { /* Check non-adult options */ for (i = 0; i < OPT_ADULT; i++) { if (option_text[i] && streq(option_text[i], buf + 2)) { op_ptr->opt[i] = FALSE; return (0); } } } /* Process "Y:" -- turn option on */ else if (buf[0] == 'Y') { /* Check non-adult options */ for (i = 0; i < OPT_ADULT; i++) { if (option_text[i] && streq(option_text[i], buf + 2)) { op_ptr->opt[i] = TRUE; return (0); } } } /* Process "W:::" -- window flags */ else if (buf[0] == 'W') { int win, flag, value; if (tokenize(buf + 2, 3, zz) == 3) { win = strtol(zz[0], NULL, 0); flag = strtol(zz[1], NULL, 0); value = strtol(zz[2], NULL, 0); /* Ignore illegal windows */ /* Hack -- Ignore the main window */ if ((win <= 0) || (win >= 8)) return (1); /* Ignore illegal flags */ if ((flag < 0) || (flag >= 32)) return (1); /* Require a real flag */ if (window_flag_desc[flag]) { if (value) { /* Turn flag on */ op_ptr->window_flag[win] |= (1L << flag); } else { /* Turn flag off */ op_ptr->window_flag[win] &= ~(1L << flag); } } /* Success */ return (0); } } /* Failure */ return (1); } /* * Helper function for "process_pref_file()" * * Input: * v: output buffer array * f: final character * * Output: * result */ static cptr process_pref_file_expr(char **sp, char *fp) { cptr v; char *b; char *s; char b1 = '['; char b2 = ']'; char f = ' '; /* Initial */ s = (*sp); /* Skip spaces */ while (isspace(*s)) s++; /* Save start */ b = s; /* Default */ v = "?o?o?"; /* Analyze */ if (*s == b1) { const char *p; const char *t; /* Skip b1 */ s++; /* First */ t = process_pref_file_expr(&s, &f); /* Oops */ if (!*t) { /* Nothing */ } /* Function: IOR */ else if (streq(t, "IOR")) { v = "0"; while (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); if (*t && !streq(t, "0")) v = "1"; } } /* Function: AND */ else if (streq(t, "AND")) { v = "1"; while (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); if (*t && streq(t, "0")) v = "0"; } } /* Function: NOT */ else if (streq(t, "NOT")) { v = "1"; while (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); if (*t && !streq(t, "0")) v = "0"; } } /* Function: EQU */ else if (streq(t, "EQU")) { v = "1"; if (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); } while (*s && (f != b2)) { p = t; t = process_pref_file_expr(&s, &f); if (*t && !streq(p, t)) v = "0"; } } /* Function: LEQ */ else if (streq(t, "LEQ")) { v = "1"; if (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); } while (*s && (f != b2)) { p = t; t = process_pref_file_expr(&s, &f); if (*t && (strcmp(p, t) >= 0)) v = "0"; } } /* Function: GEQ */ else if (streq(t, "GEQ")) { v = "1"; if (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); } while (*s && (f != b2)) { p = t; t = process_pref_file_expr(&s, &f); if (*t && (strcmp(p, t) <= 0)) v = "0"; } } /* Oops */ else { while (*s && (f != b2)) { t = process_pref_file_expr(&s, &f); } } /* Verify ending */ if (f != b2) v = "?x?x?"; /* Extract final and Terminate */ if ((f = *s) != '\0') *s++ = '\0'; } /* Other */ else { /* Accept all printables except spaces and brackets */ while (isprint(*s) && !strchr(" []", *s)) ++s; /* Extract final and Terminate */ if ((f = *s) != '\0') *s++ = '\0'; /* Variable */ if (*b == '$') { /* System */ if (streq(b+1, "SYS")) { v = ANGBAND_SYS; } /* Graphics */ else if (streq(b+1, "GRAF")) { v = ANGBAND_GRAF; } /* Race */ else if (streq(b+1, "RACE")) { v = rp_ptr->title; } /* Class */ else if (streq(b+1, "CLASS")) { v = cp_ptr->title; } /* Player */ else if (streq(b+1, "PLAYER")) { v = op_ptr->base_name; } } /* Constant */ else { v = b; } } /* Save */ (*fp) = f; /* Save */ (*sp) = s; /* Result */ return (v); } /* * Process the "user pref file" with the given name * * See the function above for a list of legal "commands". * * We also accept the special "?" and "%" directives, which * allow conditional evaluation and filename inclusion. */ errr process_pref_file(cptr name) { FILE *fp; char buf[1024]; char old[1024]; int num = -1; errr err = 0; bool bypass = FALSE; /* Build the filename */ path_build(buf, 1024, ANGBAND_DIR_USER, name); /* Open the file */ fp = my_fopen(buf, "r"); /* No such file */ if (!fp) return (-1); /* Process the file */ while (0 == my_fgets(fp, buf, 1024)) { /* Count lines */ num++; /* Skip "empty" lines */ if (!buf[0]) continue; /* Skip "blank" lines */ if (isspace(buf[0])) continue; /* Skip comments */ if (buf[0] == '#') continue; /* Save a copy */ strcpy(old, buf); /* Process "?:" */ if ((buf[0] == '?') && (buf[1] == ':')) { char f; cptr v; char *s; /* Start */ s = buf + 2; /* Parse the expr */ v = process_pref_file_expr(&s, &f); /* Set flag */ bypass = (streq(v, "0") ? TRUE : FALSE); /* Continue */ continue; } /* Apply conditionals */ if (bypass) continue; /* Process "%:" */ if (buf[0] == '%') { /* Process that file if allowed */ (void)process_pref_file(buf + 2); /* Continue */ continue; } /* Process the line */ err = process_pref_file_aux(buf); /* Oops */ if (err) break; } /* Error */ if (err) { /* Useful error message */ msg_format("Error %d in line %d of file '%s'.", err, num, name); msg_format("Parsing '%s'", old); msg_print(NULL); } /* Close the file */ my_fclose(fp); /* Result */ return (err); } #ifdef CHECK_TIME /* * Operating hours for ANGBAND (defaults to non-work hours) */ static char days[7][29] = { "SUN:XXXXXXXXXXXXXXXXXXXXXXXX", "MON:XXXXXXXX.........XXXXXXX", "TUE:XXXXXXXX.........XXXXXXX", "WED:XXXXXXXX.........XXXXXXX", "THU:XXXXXXXX.........XXXXXXX", "FRI:XXXXXXXX.........XXXXXXX", "SAT:XXXXXXXXXXXXXXXXXXXXXXXX" }; /* * Restict usage (defaults to no restrictions) */ static bool check_time_flag = FALSE; #endif /* * Handle CHECK_TIME */ errr check_time(void) { #ifdef CHECK_TIME time_t c; struct tm *tp; /* No restrictions */ if (!check_time_flag) return (0); /* Check for time violation */ c = time((time_t *)0); tp = localtime(&c); /* Violation */ if (days[tp->tm_wday][tp->tm_hour + 4] != 'X') return (1); #endif /* Success */ return (0); } /* * Initialize CHECK_TIME */ errr check_time_init(void) { #ifdef CHECK_TIME FILE *fp; char buf[1024]; /* Build the filename */ path_build(buf, 1024, ANGBAND_DIR_FILE, "time.txt"); /* Open the file */ fp = my_fopen(buf, "r"); /* No file, no restrictions */ if (!fp) return (0); /* Assume restrictions */ check_time_flag = TRUE; /* Parse the file */ while (0 == my_fgets(fp, buf, 80)) { /* Skip comments and blank lines */ if (!buf[0] || (buf[0] == '#')) continue; /* Chop the buffer */ buf[29] = '\0'; /* Extract the info */ if (prefix(buf, "SUN:")) strcpy(days[0], buf); if (prefix(buf, "MON:")) strcpy(days[1], buf); if (prefix(buf, "TUE:")) strcpy(days[2], buf); if (prefix(buf, "WED:")) strcpy(days[3], buf); if (prefix(buf, "THU:")) strcpy(days[4], buf); if (prefix(buf, "FRI:")) strcpy(days[5], buf); if (prefix(buf, "SAT:")) strcpy(days[6], buf); } /* Close it */ my_fclose(fp); #endif /* Success */ return (0); } #ifdef CHECK_LOAD #ifndef MAXHOSTNAMELEN # define MAXHOSTNAMELEN 64 #endif typedef struct statstime statstime; struct statstime { int cp_time[4]; int dk_xfer[4]; unsigned int v_pgpgin; unsigned int v_pgpgout; unsigned int v_pswpin; unsigned int v_pswpout; unsigned int v_intr; int if_ipackets; int if_ierrors; int if_opackets; int if_oerrors; int if_collisions; unsigned int v_swtch; long avenrun[3]; struct timeval boottime; struct timeval curtime; }; /* * Maximal load (if any). */ static int check_load_value = 0; #endif /* * Handle CHECK_LOAD */ errr check_load(void) { #ifdef CHECK_LOAD struct statstime st; /* Success if not checking */ if (!check_load_value) return (0); /* Check the load */ if (0 == rstat("localhost", &st)) { long val1 = (long)(st.avenrun[2]); long val2 = (long)(check_load_value) * FSCALE; /* Check for violation */ if (val1 >= val2) return (1); } #endif /* Success */ return (0); } /* * Initialize CHECK_LOAD */ errr check_load_init(void) { #ifdef CHECK_LOAD FILE *fp; char buf[1024]; char temphost[MAXHOSTNAMELEN+1]; char thishost[MAXHOSTNAMELEN+1]; /* Build the filename */ path_build(buf, 1024, ANGBAND_DIR_FILE, "load.txt"); /* Open the "load" file */ fp = my_fopen(buf, "r"); /* No file, no restrictions */ if (!fp) return (0); /* Default load */ check_load_value = 100; /* Get the host name */ (void)gethostname(thishost, (sizeof thishost) - 1); /* Parse it */ while (0 == my_fgets(fp, buf, 1024)) { int value; /* Skip comments and blank lines */ if (!buf[0] || (buf[0] == '#')) continue; /* Parse, or ignore */ if (sscanf(buf, "%s%d", temphost, &value) != 2) continue; /* Skip other hosts */ if (!streq(temphost, thishost) && !streq(temphost, "localhost")) continue; /* Use that value */ check_load_value = value; /* Done */ break; } /* Close the file */ my_fclose(fp); #endif /* Success */ return (0); } /* * Hack -- pass color info around this file */ static byte likert_color = TERM_WHITE; /* * Returns a "rating" of x depending on y */ static cptr likert(int x, int y) { /* Paranoia */ if (y <= 0) y = 1; /* Negative value */ if (x < 0) { likert_color = TERM_RED; return ("Very Bad"); } /* Analyze the value */ switch ((x / y)) { case 0: case 1: { likert_color = TERM_RED; return ("Bad"); } case 2: { likert_color = TERM_RED; return ("Poor"); } case 3: case 4: { likert_color = TERM_YELLOW; return ("Fair"); } case 5: { likert_color = TERM_YELLOW; return ("Good"); } case 6: { likert_color = TERM_YELLOW; return ("Very Good"); } case 7: case 8: { likert_color = TERM_L_GREEN; return ("Excellent"); } case 9: case 10: case 11: case 12: case 13: { likert_color = TERM_L_GREEN; return ("Superb"); } case 14: case 15: case 16: case 17: { likert_color = TERM_L_GREEN; return ("Heroic"); } default: { likert_color = TERM_L_GREEN; return ("Legendary"); } } } /* * Prints some "extra" information on the screen. * * Space includes rows 3-9 cols 24-79 * Space includes rows 10-17 cols 1-79 * Space includes rows 19-22 cols 1-79 */ static void display_player_xtra_info(void) { int col; int hit, dam; int base, plus; int i, tmp; int xthn, xthb, xfos, xsrh; int xdis, xdev, xsav, xstl; object_type *o_ptr; cptr desc; char buf[160]; /* Upper middle */ col = 26; /* Age */ Term_putstr(col, 3, -1, TERM_WHITE, "Age"); Term_putstr(col+9, 3, -1, TERM_L_BLUE, format("%4d", (int)p_ptr->age)); /* Height */ Term_putstr(col, 4, -1, TERM_WHITE, "Height"); Term_putstr(col+9, 4, -1, TERM_L_BLUE, format("%4d", (int)p_ptr->ht)); /* Weight */ Term_putstr(col, 5, -1, TERM_WHITE, "Weight"); Term_putstr(col+9, 5, -1, TERM_L_BLUE, format("%4d", (int)p_ptr->wt)); /* Status */ Term_putstr(col, 6, -1, TERM_WHITE, "Status"); Term_putstr(col+9, 6, -1, TERM_L_BLUE, format("%4d", (int)p_ptr->sc)); /* Maximize */ Term_putstr(col, 7, -1, TERM_WHITE, "Maximize"); Term_putstr(col+12, 7, -1, TERM_L_BLUE, adult_maximize ? "Y" : "N"); /* Preserve */ Term_putstr(col, 8, -1, TERM_WHITE, "Preserve"); Term_putstr(col+12, 8, -1, TERM_L_BLUE, adult_preserve ? "Y" : "N"); /* Left */ col = 1; /* Level */ Term_putstr(col, 10, -1, TERM_WHITE, "Level"); if (p_ptr->lev >= p_ptr->max_lev) { Term_putstr(col+8, 10, -1, TERM_L_GREEN, format("%10d", p_ptr->lev)); } else { Term_putstr(col+8, 10, -1, TERM_YELLOW, format("%10d", p_ptr->lev)); } /* Current Experience */ Term_putstr(col, 11, -1, TERM_WHITE, "Cur Exp"); if (p_ptr->exp >= p_ptr->max_exp) { Term_putstr(col+8, 11, -1, TERM_L_GREEN, format("%10ld", p_ptr->exp)); } else { Term_putstr(col+8, 11, -1, TERM_YELLOW, format("%10ld", p_ptr->exp)); } /* Maximum Experience */ Term_putstr(col, 12, -1, TERM_WHITE, "Max Exp"); Term_putstr(col+8, 12, -1, TERM_L_GREEN, format("%10ld", p_ptr->max_exp)); /* Advance Experience */ Term_putstr(col, 13, -1, TERM_WHITE, "Adv Exp"); if (p_ptr->lev < PY_MAX_LEVEL) { s32b advance = (player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L); Term_putstr(col+8, 13, -1, TERM_L_GREEN, format("%10ld", advance)); } else { Term_putstr(col+8, 13, -1, TERM_L_GREEN, format("%10s", "********")); } /* Gold */ Term_putstr(col, 15, -1, TERM_WHITE, "Gold"); Term_putstr(col+8, 15, -1, TERM_L_GREEN, format("%10ld", p_ptr->au)); /* Burden */ sprintf(buf, "%d.%d lbs", p_ptr->total_weight / 10, p_ptr->total_weight % 10); Term_putstr(col, 17, -1, TERM_WHITE, "Burden"); Term_putstr(col+8, 17, -1, TERM_L_GREEN, format("%10s", buf)); /* Middle */ col = 26; /* Armor */ base = p_ptr->dis_ac; plus = p_ptr->dis_to_a; /* Total Armor */ sprintf(buf, "[%d,%+d]", base, plus); Term_putstr(col, 10, -1, TERM_WHITE, "Armor"); Term_putstr(col+5, 10, -1, TERM_L_BLUE, format("%13s", buf)); /* Base skill */ hit = p_ptr->dis_to_h; dam = p_ptr->dis_to_d; /* Basic fighting */ sprintf(buf, "(%+d,%+d)", hit, dam); Term_putstr(col, 11, -1, TERM_WHITE, "Fight"); Term_putstr(col+5, 11, -1, TERM_L_BLUE, format("%13s", buf)); /* Melee weapon */ o_ptr = &inventory[INVEN_WIELD]; /* Base skill */ hit = p_ptr->dis_to_h; dam = p_ptr->dis_to_d; /* Apply weapon bonuses */ if (object_known_p(o_ptr)) hit += o_ptr->to_h; if (object_known_p(o_ptr)) dam += o_ptr->to_d; /* Melee attacks */ sprintf(buf, "(%+d,%+d)", hit, dam); Term_putstr(col, 12, -1, TERM_WHITE, "Melee"); Term_putstr(col+5, 12, -1, TERM_L_BLUE, format("%13s", buf)); /* Range weapon */ o_ptr = &inventory[INVEN_BOW]; /* Base skill */ hit = p_ptr->dis_to_h; dam = 0; /* Apply weapon bonuses */ if (object_known_p(o_ptr)) hit += o_ptr->to_h; if (object_known_p(o_ptr)) dam += o_ptr->to_d; /* Range attacks */ sprintf(buf, "(%+d,%+d)", hit, dam); Term_putstr(col, 13, -1, TERM_WHITE, "Shoot"); Term_putstr(col+5, 13, -1, TERM_L_BLUE, format("%13s", buf)); /* Blows */ sprintf(buf, "%d/turn", p_ptr->num_blow); Term_putstr(col, 14, -1, TERM_WHITE, "Blows"); Term_putstr(col+5, 14, -1, TERM_L_BLUE, format("%13s", buf)); /* Shots */ sprintf(buf, "%d/turn", p_ptr->num_fire); Term_putstr(col, 15, -1, TERM_WHITE, "Shots"); Term_putstr(col+5, 15, -1, TERM_L_BLUE, format("%13s", buf)); /* Infra */ sprintf(buf, "%d ft", p_ptr->see_infra * 10); Term_putstr(col, 17, -1, TERM_WHITE, "Infra"); Term_putstr(col+5, 17, -1, TERM_L_BLUE, format("%13s", buf)); /* Right */ col = 49; /* Fighting Skill (with current weapon) */ o_ptr = &inventory[INVEN_WIELD]; tmp = p_ptr->to_h + o_ptr->to_h; xthn = p_ptr->skill_thn + (tmp * BTH_PLUS_ADJ); /* Shooting Skill (with current bow) */ o_ptr = &inventory[INVEN_BOW]; tmp = p_ptr->to_h + o_ptr->to_h; xthb = p_ptr->skill_thb + (tmp * BTH_PLUS_ADJ); /* Basic abilities */ xdis = p_ptr->skill_dis; xdev = p_ptr->skill_dev; xsav = p_ptr->skill_sav; xstl = p_ptr->skill_stl; xsrh = p_ptr->skill_srh; xfos = p_ptr->skill_fos; put_str("Saving Throw", 10, col); desc = likert(xsav, 6); c_put_str(likert_color, format("%9s", desc), 10, col+14); put_str("Stealth", 11, col); desc = likert(xstl, 1); c_put_str(likert_color, format("%9s", desc), 11, col+14); put_str("Fighting", 12, col); desc = likert(xthn, 12); c_put_str(likert_color, format("%9s", desc), 12, col+14); put_str("Shooting", 13, col); desc = likert(xthb, 12); c_put_str(likert_color, format("%9s", desc), 13, col+14); put_str("Disarming", 14, col); desc = likert(xdis, 8); c_put_str(likert_color, format("%9s", desc), 14, col+14); put_str("Magic Device", 15, col); desc = likert(xdev, 6); c_put_str(likert_color, format("%9s", desc), 15, col+14); put_str("Perception", 16, col); desc = likert(xfos, 6); c_put_str(likert_color, format("%9s", desc), 16, col+14); put_str("Searching", 17, col); desc = likert(xsrh, 6); c_put_str(likert_color, format("%9s", desc), 17, col+14); /* Bottom */ col = 5; /* History */ for (i = 0; i < 4; i++) { put_str(p_ptr->history[i], i + 19, col); } } /* * Obtain the "flags" for the player as if he was an item */ static void player_flags(u32b *f1, u32b *f2, u32b *f3) { /* Clear */ (*f1) = (*f2) = (*f3) = 0L; /* Elf */ if (p_ptr->prace == RACE_ELF) (*f2) |= (TR2_RES_LITE); /* Hobbit */ if (p_ptr->prace == RACE_HOBBIT) (*f2) |= (TR2_SUST_DEX); /* Gnome */ if (p_ptr->prace == RACE_GNOME) (*f3) |= (TR3_FREE_ACT); /* Dwarf */ if (p_ptr->prace == RACE_DWARF) (*f2) |= (TR2_RES_BLIND); /* Half-Orc */ if (p_ptr->prace == RACE_HALF_ORC) (*f2) |= (TR2_RES_DARK); /* Half-Troll */ if (p_ptr->prace == RACE_HALF_TROLL) (*f2) |= (TR2_SUST_STR); /* Dunadan */ if (p_ptr->prace == RACE_DUNADAN) (*f2) |= (TR2_SUST_CON); /* High Elf */ if (p_ptr->prace == RACE_HIGH_ELF) (*f2) |= (TR2_RES_LITE); if (p_ptr->prace == RACE_HIGH_ELF) (*f3) |= (TR3_SEE_INVIS); if (p_ptr->pclass == CLASS_WARRIOR) { if (p_ptr->lev >= 30) (*f2) |= (TR2_RES_FEAR); } } /* * Equippy chars */ static void display_player_equippy(int y, int x) { int i; byte a; char c; object_type *o_ptr; /* Dump equippy chars */ for (i = INVEN_WIELD; i < INVEN_TOTAL; ++i) { /* Object */ o_ptr = &inventory[i]; /* Skip empty objects */ if (!o_ptr->k_idx) continue; /* Get attr/char for display */ a = object_attr(o_ptr); c = object_char(o_ptr); /* Dump */ Term_putch(x+i-INVEN_WIELD, y, a, c); } } /* * Hack -- see below */ static byte display_player_flag_set[4] = { 2, 2, 3, 1 }; /* * Hack -- see below */ static u32b display_player_flag_head[4] = { TR2_RES_ACID, TR2_RES_BLIND, TR3_SLOW_DIGEST, TR1_STEALTH }; /* * Hack -- see below */ static cptr display_player_flag_names[4][8] = { { " Acid:", /* TR2_RES_ACID */ " Elec:", /* TR2_RES_ELEC */ " Fire:", /* TR2_RES_FIRE */ " Cold:", /* TR2_RES_COLD */ " Pois:", /* TR2_RES_POIS */ " Fear:", /* TR2_RES_FEAR */ " Lite:", /* TR2_RES_LITE */ " Dark:" /* TR2_RES_DARK */ }, { "Blind:", /* TR2_RES_BLIND */ "Confu:", /* TR2_RES_CONFU */ "Sound:", /* TR2_RES_SOUND */ "Shard:", /* TR2_RES_SHARD */ "Nexus:", /* TR2_RES_NEXUS */ "Nethr:", /* TR2_RES_NETHR */ "Chaos:", /* TR2_RES_CHAOS */ "Disen:" /* TR2_RES_DISEN */ }, { "S.Dig:", /* TR3_SLOW_DIGEST */ "Feath:", /* TR3_FEATHER */ "PLite:", /* TR3_LITE */ "Regen:", /* TR3_REGEN */ "Telep:", /* TR3_TELEPATHY */ "Invis:", /* TR3_SEE_INVIS */ "FrAct:", /* TR3_FREE_ACT */ "HLife:" /* TR3_HOLD_LIFE */ }, { "Stea.:", /* TR1_STEALTH */ "Sear.:", /* TR1_SEARCH */ "Infra:", /* TR1_INFRA */ "Tunn.:", /* TR1_TUNNEL */ "Speed:", /* TR1_SPEED */ "Blows:", /* TR1_BLOWS */ "Shots:", /* TR1_SHOTS */ "Might:" /* TR1_MIGHT */ } }; /* * Special display, part 1 */ static void display_player_flag_info(void) { int x, y, i, n; int row, col; int set; u32b head; u32b flag; cptr name; u32b f[4]; /* Four columns */ for (x = 0; x < 4; x++) { /* Reset */ row = 11; col = 20 * x; /* Extract set */ set = display_player_flag_set[x]; /* Extract head */ head = display_player_flag_head[x]; /* Header */ c_put_str(TERM_WHITE, "abcdefghijkl@", row++, col+6); /* Eight rows */ for (y = 0; y < 8; y++) { /* Extract flag */ flag = (head << y); /* Extract name */ name = display_player_flag_names[x][y]; /* Header */ c_put_str(TERM_WHITE, name, row, col); /* Check equipment */ for (n = 6, i = INVEN_WIELD; i < INVEN_TOTAL; ++i, ++n) { byte attr = TERM_SLATE; object_type *o_ptr; /* Object */ o_ptr = &inventory[i]; /* Known flags */ object_flags_known(o_ptr, &f[1], &f[2], &f[3]); /* Color columns by parity */ if (i % 2) attr = TERM_L_WHITE; /* Non-existant objects */ if (!o_ptr->k_idx) attr = TERM_L_DARK; /* Hack -- Check immunities */ if ((x == 0) && (y < 4) && (f[set] & ((TR2_IM_ACID) << y))) { c_put_str(TERM_WHITE, "*", row, col+n); } /* Check flags */ else if (f[set] & flag) { c_put_str(TERM_WHITE, "+", row, col+n); } /* Default */ else { c_put_str(attr, ".", row, col+n); } } /* Player flags */ player_flags(&f[1], &f[2], &f[3]); /* Default */ c_put_str(TERM_SLATE, ".", row, col+n); /* Check flags */ if (f[set] & flag) c_put_str(TERM_WHITE, "+", row, col+n); /* Advance */ row++; } /* Footer */ c_put_str(TERM_WHITE, "abcdefghijkl@", row++, col+6); /* Equippy */ display_player_equippy(row++, col+6); } } /* * Special display, part 2a */ static void display_player_misc_info(void) { cptr p; char buf[80]; /* Name */ put_str("Name", 2, 1); c_put_str(TERM_L_BLUE, op_ptr->full_name, 2, 8); /* Sex */ put_str("Sex", 3, 1); c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 8); /* Race */ put_str("Race", 4, 1); c_put_str(TERM_L_BLUE, rp_ptr->title, 4, 8); /* Class */ put_str("Class", 5, 1); c_put_str(TERM_L_BLUE, cp_ptr->title, 5, 8); /* Title */ put_str("Title", 6, 1); /* Wizard */ if (p_ptr->wizard) { p = "[=-WIZARD-=]"; } /* Winner */ else if (p_ptr->total_winner || (p_ptr->lev > PY_MAX_LEVEL)) { p = "***WINNER***"; } /* Normal */ else { p = player_title[p_ptr->pclass][(p_ptr->lev-1)/5]; } /* Dump it */ c_put_str(TERM_L_BLUE, p, 6, 8); /* Hit Points */ put_str("HP", 7, 1); sprintf(buf, "%d/%d", p_ptr->chp, p_ptr->mhp); c_put_str(TERM_L_BLUE, buf, 7, 8); /* Spell Points */ put_str("SP", 8, 1); sprintf(buf, "%d/%d", p_ptr->csp, p_ptr->msp); c_put_str(TERM_L_BLUE, buf, 8, 8); } /* * Special display, part 2b */ static void display_player_stat_info(void) { int i, row, col; char buf[80]; /* Row */ row = 3; /* Column */ col = 42; /* Print out the labels for the columns */ c_put_str(TERM_WHITE, " Self", row-1, col+5); c_put_str(TERM_WHITE, " RB", row-1, col+12); c_put_str(TERM_WHITE, " CB", row-1, col+16); c_put_str(TERM_WHITE, " EB", row-1, col+20); c_put_str(TERM_WHITE, " Best", row-1, col+24); /* Display the stats */ for (i = 0; i < A_MAX; i++) { /* Reduced */ if (p_ptr->stat_use[i] < p_ptr->stat_top[i]) { /* Use lowercase stat name */ put_str(stat_names_reduced[i], row+i, col); } /* Normal */ else { /* Assume uppercase stat name */ put_str(stat_names[i], row+i, col); } /* Indicate natural maximum */ if (p_ptr->stat_max[i] == 18+100) { put_str("!", row+i, col+3); } /* Internal "natural" maximum value */ cnv_stat(p_ptr->stat_max[i], buf); c_put_str(TERM_L_GREEN, buf, row+i, col+5); /* Race Bonus */ sprintf(buf, "%+3d", rp_ptr->r_adj[i]); c_put_str(TERM_L_BLUE, buf, row+i, col+12); /* Class Bonus */ sprintf(buf, "%+3d", cp_ptr->c_adj[i]); c_put_str(TERM_L_BLUE, buf, row+i, col+16); /* Equipment Bonus */ sprintf(buf, "%+3d", p_ptr->stat_add[i]); c_put_str(TERM_L_BLUE, buf, row+i, col+20); /* Resulting "modified" maximum value */ cnv_stat(p_ptr->stat_top[i], buf); c_put_str(TERM_L_GREEN, buf, row+i, col+24); /* Only display stat_use if not maximal */ if (p_ptr->stat_use[i] < p_ptr->stat_top[i]) { cnv_stat(p_ptr->stat_use[i], buf); c_put_str(TERM_YELLOW, buf, row+i, col+31); } } } /* * Special display, part 2c * * How to print out the modifications and sustains. * Positive mods with no sustain will be light green. * Positive mods with a sustain will be dark green. * Sustains (with no modification) will be a dark green 's'. * Negative mods (from a curse) will be red. * Huge mods (>9), like from MICoMorgoth, will be a '*' * No mod, no sustain, will be a slate '.' */ static void display_player_sust_info(void) { int i, row, col, stat; object_type *o_ptr; u32b f1, f2, f3; u32b ignore_f2, ignore_f3; s16b k_idx; byte a; char c; /* Row */ row = 3; /* Column */ col = 26; /* Header */ c_put_str(TERM_WHITE, "abcdefghijkl@", row-1, col); /* Process equipment */ for (i = INVEN_WIELD; i < INVEN_TOTAL; ++i) { /* Get the object */ o_ptr = &inventory[i]; /* Object kind */ k_idx = o_ptr->k_idx; /* Get the "known" flags */ object_flags_known(o_ptr, &f1, &f2, &f3); /* Hack -- assume stat modifiers are known */ object_flags(o_ptr, &f1, &ignore_f2, &ignore_f3); /* Initialize color based of sign of pval. */ for (stat = 0; stat < A_MAX; stat++) { /* Default */ a = TERM_SLATE; c = '.'; /* Boost */ if (f1 & 1<pval > 0) { /* Good */ a = TERM_L_GREEN; /* Label boost */ if (o_ptr->pval < 10) c = I2D(o_ptr->pval); } /* Bad */ if (o_ptr->pval < 0) { /* Bad */ a = TERM_RED; /* Label boost */ if (o_ptr->pval > -10) c = I2D(-(o_ptr->pval)); } } /* Sustain */ if (f2 & 1<lev), 9, 8); /* Stat/Sustain flags */ display_player_sust_info(); /* Other flags */ display_player_flag_info(); } /* Standard */ else { /* Extra info */ display_player_xtra_info(); } } /* * Hack -- Dump a character description file * * XXX XXX XXX Allow the "full" flag to dump additional info, * and trigger its usage from various places in the code. */ errr file_character(cptr name, bool full) { int i, x, y; byte a; char c; #if 0 cptr other = "("; #endif cptr paren = ")"; int fd = -1; FILE *fff = NULL; store_type *st_ptr = &store[STORE_HOME]; char o_name[80]; char buf[1024]; /* Drop priv's */ safe_setuid_drop(); /* Build the filename */ path_build(buf, 1024, ANGBAND_DIR_USER, name); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Check for existing file */ fd = fd_open(buf, O_RDONLY); /* Existing file */ if (fd >= 0) { char out_val[160]; /* Close the file */ fd_close(fd); /* Build query */ sprintf(out_val, "Replace existing file %s? ", buf); /* Ask */ if (get_check(out_val)) fd = -1; } /* Open the non-existing file */ if (fd < 0) fff = my_fopen(buf, "w"); /* Grab priv's */ safe_setuid_grab(); /* Invalid file */ if (!fff) { /* Message */ msg_format("Character dump failed!"); msg_print(NULL); /* Error */ return (-1); } /* Begin dump */ fprintf(fff, " [Angband %d.%d.%d Character Dump]\n\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); /* Display player */ display_player(0); /* Dump part of the screen */ for (y = 2; y < 23; y++) { /* Dump each row */ for (x = 0; x < 79; x++) { /* Get the attr/char */ (void)(Term_what(x, y, &a, &c)); /* Dump it */ buf[x] = c; } /* Back up over spaces */ while ((x > 0) && (buf[x-1] == ' ')) --x; /* Terminate */ buf[x] = '\0'; /* End the row */ fprintf(fff, "%s\n", buf); } /* Skip some lines */ fprintf(fff, "\n\n"); /* Dump the equipment */ if (p_ptr->equip_cnt) { fprintf(fff, " [Character Equipment]\n\n"); for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) { object_desc(o_name, &inventory[i], TRUE, 3); fprintf(fff, "%c%s %s\n", index_to_label(i), paren, o_name); } fprintf(fff, "\n\n"); } /* Dump the inventory */ fprintf(fff, " [Character Inventory]\n\n"); for (i = 0; i < INVEN_PACK; i++) { object_desc(o_name, &inventory[i], TRUE, 3); fprintf(fff, "%c%s %s\n", index_to_label(i), paren, o_name); } fprintf(fff, "\n\n"); /* Dump the Home (page 1) */ fprintf(fff, " [Home Inventory (page 1)]\n\n"); for (i = 0; i < 12; i++) { object_desc(o_name, &st_ptr->stock[i], TRUE, 3); fprintf(fff, "%c%s %s\n", I2A(i%12), paren, o_name); } fprintf(fff, "\n\n"); /* Dump the Home (page 2) */ fprintf(fff, " [Home Inventory (page 2)]\n\n"); for (i = 12; i < 24; i++) { object_desc(o_name, &st_ptr->stock[i], TRUE, 3); fprintf(fff, "%c%s %s\n", I2A(i%12), paren, o_name); } fprintf(fff, "\n\n"); /* Dump options */ fprintf(fff, " [Options]\n\n"); /* Dump options */ for (i = 0; i < OPT_MAX; i++) { if (option_desc[i]) { fprintf(fff, "%-48s: %s (%s)\n", option_desc[i], op_ptr->opt[i] ? "yes" : "no ", option_text[i]); } } /* Skip some lines */ fprintf(fff, "\n\n"); /* Close it */ my_fclose(fff); /* Message */ msg_print("Character dump successful."); msg_print(NULL); /* Success */ return (0); } /* * Recursive file perusal. * * Return FALSE on "ESCAPE", otherwise TRUE. * * Process various special text in the input file, including the "menu" * structures used by the "help file" system. * * This function could be made much more efficient with the use of "seek" * functionality, especially when moving backwards through a file, or * forwards through a file by less than a page at a time. XXX XXX XXX * * Consider using a temporary file, in which special lines do not appear, * and which could be pre-padded to 80 characters per line, to allow the * use of perfect seeking. XXX XXX XXX * * Allow the user to "save" the current file. XXX XXX XXX */ bool show_file(cptr name, cptr what, int line, int mode) { int i, k; char ch = '\0'; /* Number of "real" lines passed by */ int next = 0; /* Number of "real" lines in the file */ int size = 0; /* Backup value for "line" */ int back = 0; /* This screen has sub-screens */ bool menu = FALSE; /* Current help file */ FILE *fff = NULL; /* Find this string (if any) */ cptr find = NULL; /* Hold a string to find */ char finder[80]; /* Hold a string to show */ char shower[80]; /* Describe this thing */ char caption[128]; /* Path buffer */ char path[1024]; /* General buffer */ char buf[1024]; /* Sub-menu information */ char hook[10][32]; /* Wipe finder */ strcpy(finder, ""); /* Wipe shower */ strcpy(shower, ""); /* Wipe caption */ strcpy(caption, ""); /* Wipe the hooks */ for (i = 0; i < 10; i++) hook[i][0] = '\0'; /* Hack XXX XXX XXX */ if (what) { /* Caption */ strcpy(caption, what); /* Get the filename */ strcpy(path, name); /* Open */ fff = my_fopen(path, "r"); } /* Look in "help" */ if (!fff) { /* Caption */ sprintf(caption, "Help file '%s'", name); /* Build the filename */ path_build(path, 1024, ANGBAND_DIR_HELP, name); /* Open the file */ fff = my_fopen(path, "r"); } /* Look in "info" */ if (!fff) { /* Caption */ sprintf(caption, "Info file '%s'", name); /* Build the filename */ path_build(path, 1024, ANGBAND_DIR_INFO, name); /* Open the file */ fff = my_fopen(path, "r"); } /* Oops */ if (!fff) { /* Message */ msg_format("Cannot open '%s'.", name); msg_print(NULL); /* Oops */ return (TRUE); } /* Pre-Parse the file */ while (TRUE) { /* Read a line or stop */ if (my_fgets(fff, buf, 1024)) break; /* XXX Parse "menu" items */ if (prefix(buf, "***** ")) { char b1 = '[', b2 = ']'; /* Notice "menu" requests */ if ((buf[6] == b1) && isdigit(buf[7]) && (buf[8] == b2) && (buf[9] == ' ')) { /* This is a menu file */ menu = TRUE; /* Extract the menu item */ k = D2I(buf[7]); /* Extract the menu item */ strcpy(hook[k], buf + 10); } /* Skip this */ continue; } /* Count the "real" lines */ next++; } /* Save the number of "real" lines */ size = next; /* Display the file */ while (TRUE) { /* Clear screen */ Term_clear(); /* Restart when necessary */ if (line >= size) line = 0; /* Re-open the file if needed */ if (next > line) { /* Close it */ my_fclose(fff); /* Hack -- Re-Open the file */ fff = my_fopen(path, "r"); /* Oops */ if (!fff) return (TRUE); /* File has been restarted */ next = 0; } /* Skip lines if needed */ for (; next < line; next++) { /* Skip a line */ if (my_fgets(fff, buf, 1024)) break; } /* Dump the next 20 lines of the file */ for (i = 0; i < 20; ) { /* Hack -- track the "first" line */ if (!i) line = next; /* Get a line of the file or stop */ if (my_fgets(fff, buf, 1024)) break; /* Hack -- skip "special" lines */ if (prefix(buf, "***** ")) continue; /* Count the "real" lines */ next++; /* Hack -- keep searching */ if (find && !i && !strstr(buf, find)) continue; /* Hack -- stop searching */ find = NULL; /* Dump the line */ Term_putstr(0, i+2, -1, TERM_WHITE, buf); /* Hilite "shower" */ if (shower[0]) { cptr str = buf; /* Display matches */ while ((str = strstr(str, shower)) != NULL) { int len = strlen(shower); /* Display the match */ Term_putstr(str-buf, i+2, len, TERM_YELLOW, shower); /* Advance */ str += len; } } /* Count the printed lines */ i++; } /* Hack -- failed search */ if (find) { bell("Search string not found!"); line = back; find = NULL; continue; } /* Show a general "title" */ prt(format("[Angband %d.%d.%d, %s, Line %d/%d]", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, caption, line, size), 0, 0); /* Prompt -- menu screen */ if (menu) { /* Wait for it */ prt("[Press a Number, or ESC to exit.]", 23, 0); } /* Prompt -- small files */ else if (size <= 20) { /* Wait for it */ prt("[Press ESC to exit.]", 23, 0); } /* Prompt -- large files */ else { /* Wait for it */ prt("[Press Space to advance, or ESC to exit.]", 23, 0); } /* Get a keypress */ ch = inkey(); /* Hack -- Return to last screen */ if (ch == '?') break; /* Hack -- Try showing */ if (ch == '&') { /* Get "shower" */ prt("Show: ", 23, 0); (void)askfor_aux(shower, 80); } /* Hack -- Try finding */ if (ch == '/') { /* Get "finder" */ prt("Find: ", 23, 0); if (askfor_aux(finder, 80)) { /* Find it */ find = finder; back = line; line = line + 1; /* Show it */ strcpy(shower, finder); } } /* Hack -- Go to a specific line */ if (ch == '#') { char tmp[80]; prt("Goto Line: ", 23, 0); strcpy(tmp, "0"); if (askfor_aux(tmp, 80)) { line = atoi(tmp); } } /* Hack -- Go to a specific file */ if (ch == '%') { char ftmp[80]; prt("Goto File: ", 23, 0); strcpy(ftmp, "help.hlp"); if (askfor_aux(ftmp, 80)) { if (!show_file(ftmp, NULL, 0, mode)) ch = ESCAPE; } } /* Back up one line */ if (ch == '=') { line = line - 1; if (line < 0) line = 0; } /* Back up one half page */ if (ch == '_') { line = line - 10; if (line < 0) line = 0; } /* Back up one full page */ if (ch == '-') { line = line - 20; if (line < 0) line = 0; } /* Advance one line */ if ((ch == '\n') || (ch == '\r')) { line = line + 1; } /* Advance one half page */ if (ch == '+') { line = line + 10; if (line < 0) line = 0; } /* Advance one full page */ if (ch == ' ') { line = line + 20; } /* Recurse on numbers */ if (menu && isdigit(ch) && hook[D2I(ch)][0]) { /* Recurse on that file */ if (!show_file(hook[D2I(ch)], NULL, 0, mode)) ch = ESCAPE; } /* Exit on escape */ if (ch == ESCAPE) break; } /* Close the file */ my_fclose(fff); /* Done */ return (ch != ESCAPE); } /* * Peruse the On-Line-Help */ void do_cmd_help(void) { /* Save screen */ screen_save(); /* Peruse the main help file */ (void)show_file("help.hlp", NULL, 0, 0); /* Load screen */ screen_load(); } /* * Process the player name and extract a clean "base name". * * If "sf" is TRUE, then we initialize "savefile" based on player name. * * Some platforms (Windows, Macintosh, Amiga) leave the "savefile" empty * when a new character is created, and then when the character is done * being created, they call this function to choose a new savefile name. */ void process_player_name(bool sf) { int i; /* Cannot be too long */ if (strlen(op_ptr->full_name) > 15) { /* Name too long */ quit_fmt("The name '%s' is too long!", op_ptr->full_name); } /* Process the player name */ for (i = 0; op_ptr->full_name[i]; i++) { char c = op_ptr->full_name[i]; /* No control characters */ if (iscntrl(c)) { /* Illegal characters */ quit_fmt("Illegal control char (0x%02X) in player name", c); } /* Convert all non-alphanumeric symbols */ if (!isalpha(c) && !isdigit(c)) c = '_'; /* Build "base_name" */ op_ptr->base_name[i] = c; } #if defined(WINDOWS) || defined(MSDOS) /* Max length */ if (i > 8) i = 8; #endif /* Terminate */ op_ptr->base_name[i] = '\0'; /* Require a "base" name */ if (!op_ptr->base_name[0]) { strcpy(op_ptr->base_name, "PLAYER"); } /* Pick savefile name if needed */ if (sf) { char temp[128]; #ifdef SAVEFILE_USE_UID /* Rename the savefile, using the player_uid and base_name */ sprintf(temp, "%d.%s", player_uid, op_ptr->base_name); #else /* Rename the savefile, using the base name */ sprintf(temp, "%s", op_ptr->base_name); #endif #ifdef VM /* Hack -- support "flat directory" usage on VM/ESA */ sprintf(temp, "%s.sv", op_ptr->base_name); #endif /* VM */ /* Build the filename */ path_build(savefile, 1024, ANGBAND_DIR_SAVE, temp); } } /* * Gets a name for the character, reacting to name changes. * * Perhaps we should NOT ask for a name (at "birth()") on * Unix machines? XXX XXX XXX * * What a horrible name for a global function. XXX XXX XXX */ void get_name(void) { char tmp[16]; /* Save the player name */ strcpy(tmp, op_ptr->full_name); /* Prompt for a new name */ if (get_string("Enter a name for your character: ", tmp, 15)) { /* Use the name */ strcpy(op_ptr->full_name, tmp); /* Process the player name */ process_player_name(FALSE); } } /* * Hack -- commit suicide */ void do_cmd_suicide(void) { /* Flush input */ flush(); /* Verify Retirement */ if (p_ptr->total_winner) { /* Verify */ if (!get_check("Do you want to retire? ")) return; } /* Verify Suicide */ else { char ch; /* Verify */ if (!get_check("Do you really want to quit? ")) return; /* Special Verification for suicide */ prt("Please verify QUITTING by typing the '@' sign: ", 0, 0); flush(); ch = inkey(); prt("", 0, 0); if (ch != '@') return; } /* Commit suicide */ p_ptr->is_dead = TRUE; /* Stop playing */ p_ptr->playing = FALSE; /* Leaving */ p_ptr->leaving = TRUE; /* Cause of death */ strcpy(p_ptr->died_from, "Quitting"); } /* * Save the game */ void do_cmd_save_game(void) { /* Disturb the player */ disturb(1, 0); /* Clear messages */ msg_print(NULL); /* Handle stuff */ handle_stuff(); /* Message */ prt("Saving game...", 0, 0); /* Refresh */ Term_fresh(); /* The player is not dead */ strcpy(p_ptr->died_from, "(saved)"); /* Forbid suspend */ signals_ignore_tstp(); /* Save the player */ if (save_player()) { prt("Saving game... done.", 0, 0); } /* Save failed (oops) */ else { prt("Saving game... failed!", 0, 0); } /* Allow suspend again */ signals_handle_tstp(); /* Refresh */ Term_fresh(); /* Note that the player is not dead */ strcpy(p_ptr->died_from, "(alive and well)"); } /* * Hack -- Calculates the total number of points earned */ long total_points(void) { return (p_ptr->max_exp + (100 * p_ptr->max_depth)); } /* * Centers a string within a 31 character string */ static void center_string(char *buf, cptr str) { int i, j; /* Total length */ i = strlen(str); /* Necessary border */ j = 15 - i / 2; /* Mega-Hack */ sprintf(buf, "%*s%s%*s", j, "", str, 31 - i - j, ""); } #if 0 /* * Save a "bones" file for a dead character * * Note that we will not use these files until a later version, and * then we will only use the name and level on which death occured. * * Should probably attempt some form of locking... */ static void make_bones(void) { FILE *fp; char str[1024]; /* Ignore wizards and borgs */ if (!(p_ptr->noscore & 0x00FF)) { /* Ignore people who die in town */ if (p_ptr->depth) { char tmp[128]; /* XXX XXX XXX "Bones" name */ sprintf(tmp, "bone.%03d", p_ptr->depth); /* Build the filename */ path_build(str, 1024, ANGBAND_DIR_BONE, tmp); /* Attempt to open the bones file */ fp = my_fopen(str, "r"); /* Close it right away */ if (fp) my_fclose(fp); /* Do not over-write a previous ghost */ if (fp) return; /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Try to write a new "Bones File" */ fp = my_fopen(str, "w"); /* Not allowed to write it? Weird. */ if (!fp) return; /* Save the info */ fprintf(fp, "%s\n", op_ptr->full_name); fprintf(fp, "%d\n", p_ptr->mhp); fprintf(fp, "%d\n", p_ptr->prace); fprintf(fp, "%d\n", p_ptr->pclass); /* Close and save the Bones file */ my_fclose(fp); } } } #endif /* * Display a "tomb-stone" */ static void print_tomb(void) { cptr p; char tmp[160]; char buf[1024]; FILE *fp; time_t ct = time((time_t)0); /* Clear screen */ Term_clear(); /* Build the filename */ path_build(buf, 1024, ANGBAND_DIR_FILE, "dead.txt"); /* Open the News file */ fp = my_fopen(buf, "r"); /* Dump */ if (fp) { int i = 0; /* Dump the file to the screen */ while (0 == my_fgets(fp, buf, 1024)) { /* Display and advance */ put_str(buf, i++, 0); } /* Close */ my_fclose(fp); } /* King or Queen */ if (p_ptr->total_winner || (p_ptr->lev > PY_MAX_LEVEL)) { p = "Magnificent"; } /* Normal */ else { p = player_title[p_ptr->pclass][(p_ptr->lev-1)/5]; } center_string(buf, op_ptr->full_name); put_str(buf, 6, 11); center_string(buf, "the"); put_str(buf, 7, 11); center_string(buf, p); put_str(buf, 8, 11); center_string(buf, cp_ptr->title); put_str(buf, 10, 11); sprintf(tmp, "Level: %d", (int)p_ptr->lev); center_string(buf, tmp); put_str(buf, 11, 11); sprintf(tmp, "Exp: %ld", (long)p_ptr->exp); center_string(buf, tmp); put_str(buf, 12, 11); sprintf(tmp, "AU: %ld", (long)p_ptr->au); center_string(buf, tmp); put_str(buf, 13, 11); sprintf(tmp, "Killed on Level %d", p_ptr->depth); center_string(buf, tmp); put_str(buf, 14, 11); sprintf(tmp, "by %s.", p_ptr->died_from); center_string(buf, tmp); put_str(buf, 15, 11); sprintf(tmp, "%-.24s", ctime(&ct)); center_string(buf, tmp); put_str(buf, 17, 11); } /* * Display some character info */ static void show_info(void) { int i, j, k; object_type *o_ptr; store_type *st_ptr = &store[STORE_HOME]; /* Hack -- Know everything in the inven/equip */ for (i = 0; i < INVEN_TOTAL; i++) { o_ptr = &inventory[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Aware and Known */ object_aware(o_ptr); object_known(o_ptr); } /* Hack -- Know everything in the home */ for (i = 0; i < st_ptr->stock_num; i++) { o_ptr = &st_ptr->stock[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Aware and Known */ object_aware(o_ptr); object_known(o_ptr); } /* Hack -- Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Flush all input keys */ flush(); /* Flush messages */ msg_print(NULL); /* Describe options */ prt("You may now dump a character record to one or more files.", 21, 0); prt("Then, hit RETURN to see the character, or ESC to abort.", 22, 0); /* Dump character records as requested */ while (TRUE) { char tmp[80]; /* Prompt */ put_str("Filename: ", 23, 0); /* Default */ strcpy(tmp, ""); /* Ask for filename (or abort) */ if (!askfor_aux(tmp, 80)) return; /* Return means "show on screen" */ if (!tmp[0]) break; /* Save screen */ screen_save(); /* Dump a character file */ (void)file_character(tmp, FALSE); /* Load screen */ screen_load(); } /* Display player */ display_player(0); /* Prompt for inventory */ prt("Hit any key to see more information (ESC to abort): ", 23, 0); /* Allow abort at this point */ if (inkey() == ESCAPE) return; /* Show equipment and inventory */ /* Equipment -- if any */ if (p_ptr->equip_cnt) { Term_clear(); item_tester_full = TRUE; show_equip(); prt("You are using: -more-", 0, 0); if (inkey() == ESCAPE) return; } /* Inventory -- if any */ if (p_ptr->inven_cnt) { Term_clear(); item_tester_full = TRUE; show_inven(); prt("You are carrying: -more-", 0, 0); if (inkey() == ESCAPE) return; } /* Home -- if anything there */ if (st_ptr->stock_num) { /* Display contents of the home */ for (k = 0, i = 0; i < st_ptr->stock_num; k++) { /* Clear screen */ Term_clear(); /* Show 12 items */ for (j = 0; (j < 12) && (i < st_ptr->stock_num); j++, i++) { byte attr; char o_name[80]; char tmp_val[80]; /* Get the object */ o_ptr = &st_ptr->stock[i]; /* Print header, clear line */ sprintf(tmp_val, "%c) ", I2A(j)); prt(tmp_val, j+2, 4); /* Get the object description */ object_desc(o_name, o_ptr, TRUE, 3); /* Get the inventory color */ attr = tval_to_attr[o_ptr->tval & 0x7F]; /* Display the object */ c_put_str(attr, o_name, j+2, 7); } /* Caption */ prt(format("Your home contains (page %d): -more-", k+1), 0, 0); /* Wait for it */ if (inkey() == ESCAPE) return; } } } /* * Seek score 'i' in the highscore file */ static int highscore_seek(int i) { /* Seek for the requested record */ return (fd_seek(highscore_fd, (huge)(i) * sizeof(high_score))); } /* * Read one score from the highscore file */ static errr highscore_read(high_score *score) { /* Read the record, note failure */ return (fd_read(highscore_fd, (char*)(score), sizeof(high_score))); } /* * Write one score to the highscore file */ static int highscore_write(high_score *score) { /* Write the record, note failure */ return (fd_write(highscore_fd, (char*)(score), sizeof(high_score))); } /* * Just determine where a new score *would* be placed * Return the location (0 is best) or -1 on failure */ static int highscore_where(high_score *score) { int i; high_score the_score; /* Paranoia -- it may not have opened */ if (highscore_fd < 0) return (-1); /* Go to the start of the highscore file */ if (highscore_seek(0)) return (-1); /* Read until we get to a higher score */ for (i = 0; i < MAX_HISCORES; i++) { if (highscore_read(&the_score)) return (i); if (strcmp(the_score.pts, score->pts) < 0) return (i); } /* The "last" entry is always usable */ return (MAX_HISCORES - 1); } /* * Actually place an entry into the high score file * Return the location (0 is best) or -1 on "failure" */ static int highscore_add(high_score *score) { int i, slot; bool done = FALSE; high_score the_score, tmpscore; /* Paranoia -- it may not have opened */ if (highscore_fd < 0) return (-1); /* Determine where the score should go */ slot = highscore_where(score); /* Hack -- Not on the list */ if (slot < 0) return (-1); /* Hack -- prepare to dump the new score */ the_score = (*score); /* Slide all the scores down one */ for (i = slot; !done && (i < MAX_HISCORES); i++) { /* Read the old guy, note errors */ if (highscore_seek(i)) return (-1); if (highscore_read(&tmpscore)) done = TRUE; /* Back up and dump the score we were holding */ if (highscore_seek(i)) return (-1); if (highscore_write(&the_score)) return (-1); /* Hack -- Save the old score, for the next pass */ the_score = tmpscore; } /* Return location used */ return (slot); } /* * Display the scores in a given range. * Assumes the high score list is already open. * Only five entries per line, too much info. * * Mega-Hack -- allow "fake" entry at the given position. */ void display_scores_aux(int from, int to, int note, high_score *score) { char ch; int i, j, k, n, place; high_score the_score; char out_val[160]; char tmp_val[160]; byte attr; /* Paranoia -- it may not have opened */ if (highscore_fd < 0) return; /* Assume we will show the first 10 */ if (from < 0) from = 0; if (to < 0) to = 10; if (to > MAX_HISCORES) to = MAX_HISCORES; /* Seek to the beginning */ if (highscore_seek(0)) return; /* Hack -- Count the high scores */ for (i = 0; i < MAX_HISCORES; i++) { if (highscore_read(&the_score)) break; } /* Hack -- allow "fake" entry to be last */ if ((note == i) && score) i++; /* Forget about the last entries */ if (i > to) i = to; /* Show 5 per page, until "done" */ for (k = from, place = k+1; k < i; k += 5) { /* Clear screen */ Term_clear(); /* Title */ put_str(" Angband Hall of Fame", 0, 0); /* Indicate non-top scores */ if (k > 0) { sprintf(tmp_val, "(from position %d)", k + 1); put_str(tmp_val, 0, 40); } /* Dump 5 entries */ for (j = k, n = 0; j < i && n < 5; place++, j++, n++) { int pr, pc, clev, mlev, cdun, mdun; cptr user, gold, when, aged; /* Hack -- indicate death in yellow */ attr = (j == note) ? TERM_YELLOW : TERM_WHITE; /* Mega-Hack -- insert a "fake" record */ if ((note == j) && score) { the_score = (*score); attr = TERM_L_GREEN; score = NULL; note = -1; j--; } /* Read a normal record */ else { /* Read the proper record */ if (highscore_seek(j)) break; if (highscore_read(&the_score)) break; } /* Extract the race/class */ pr = atoi(the_score.p_r); pc = atoi(the_score.p_c); /* Extract the level info */ clev = atoi(the_score.cur_lev); mlev = atoi(the_score.max_lev); cdun = atoi(the_score.cur_dun); mdun = atoi(the_score.max_dun); /* Hack -- extract the gold and such */ for (user = the_score.uid; isspace(*user); user++) /* loop */; for (when = the_score.day; isspace(*when); when++) /* loop */; for (gold = the_score.gold; isspace(*gold); gold++) /* loop */; for (aged = the_score.turns; isspace(*aged); aged++) /* loop */; /* Dump some info */ sprintf(out_val, "%3d.%9s %s the %s %s, Level %d", place, the_score.pts, the_score.who, race_info[pr].title, class_info[pc].title, clev); /* Append a "maximum level" */ if (mlev > clev) strcat(out_val, format(" (Max %d)", mlev)); /* Dump the first line */ c_put_str(attr, out_val, n*4 + 2, 0); /* Another line of info */ sprintf(out_val, " Killed by %s on %s %d", the_score.how, "Dungeon Level", cdun); /* Hack -- some people die in the town */ if (!cdun) { sprintf(out_val, " Killed by %s in the Town", the_score.how); } /* Append a "maximum level" */ if (mdun > cdun) strcat(out_val, format(" (Max %d)", mdun)); /* Dump the info */ c_put_str(attr, out_val, n*4 + 3, 0); /* And still another line of info */ sprintf(out_val, " (User %s, Date %s, Gold %s, Turn %s).", user, when, gold, aged); c_put_str(attr, out_val, n*4 + 4, 0); } /* Wait for response */ prt("[Press ESC to quit, any other key to continue.]", 23, 17); ch = inkey(); prt("", 23, 0); /* Hack -- notice Escape */ if (ch == ESCAPE) break; } } /* * Hack -- Display the scores in a given range and quit. * * This function is only called from "main.c" when the user asks * to see the "high scores". */ void display_scores(int from, int to) { char buf[1024]; /* Build the filename */ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw"); /* Open the binary high score file, for reading */ highscore_fd = fd_open(buf, O_RDONLY); /* Clear screen */ Term_clear(); /* Title */ put_str(" Angband Hall of Fame", 0, 0); /* Display the scores */ display_scores_aux(from, to, -1, NULL); /* Shut the high score file */ fd_close(highscore_fd); /* Forget the high score fd */ highscore_fd = -1; /* Wait for response */ prt("[Press any key to quit.]", 23, 17); (void)inkey(); prt("", 23, 0); /* Quit */ quit(NULL); } /* * Enters a players name on a hi-score table, if "legal", and in any * case, displays some relevant portion of the high score list. * * Assumes "signals_ignore_tstp()" has been called. */ static errr top_twenty(void) { int j; high_score the_score; time_t ct = time((time_t*)0); /* Clear screen */ Term_clear(); /* No score file */ if (highscore_fd < 0) { msg_print("Score file unavailable."); msg_print(NULL); return (0); } #ifndef SCORE_WIZARDS /* Wizard-mode pre-empts scoring */ if (p_ptr->noscore & 0x000F) { msg_print("Score not registered for wizards."); msg_print(NULL); display_scores_aux(0, 10, -1, NULL); return (0); } #endif #ifndef SCORE_BORGS /* Borg-mode pre-empts scoring */ if (p_ptr->noscore & 0x00F0) { msg_print("Score not registered for borgs."); msg_print(NULL); display_scores_aux(0, 10, -1, NULL); return (0); } #endif #ifndef SCORE_CHEATERS /* Cheaters are not scored */ for (j = OPT_SCORE; j < OPT_MAX; ++j) { if (!op_ptr->opt[j]) continue; msg_print("Score not registered for cheaters."); msg_print(NULL); display_scores_aux(0, 10, -1, NULL); return (0); } #endif /* Hack -- Interupted */ if (!p_ptr->total_winner && streq(p_ptr->died_from, "Interrupting")) { msg_print("Score not registered due to interruption."); msg_print(NULL); display_scores_aux(0, 10, -1, NULL); return (0); } /* Hack -- Quitter */ if (!p_ptr->total_winner && streq(p_ptr->died_from, "Quitting")) { msg_print("Score not registered due to quitting."); msg_print(NULL); display_scores_aux(0, 10, -1, NULL); return (0); } /* Clear the record */ (void)WIPE(&the_score, high_score); /* Save the version */ sprintf(the_score.what, "%u.%u.%u", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); /* Calculate and save the points */ sprintf(the_score.pts, "%9lu", (long)total_points()); the_score.pts[9] = '\0'; /* Save the current gold */ sprintf(the_score.gold, "%9lu", (long)p_ptr->au); the_score.gold[9] = '\0'; /* Save the current turn */ sprintf(the_score.turns, "%9lu", (long)turn); the_score.turns[9] = '\0'; #ifdef HIGHSCORE_DATE_HACK /* Save the date in a hacked up form (9 chars) */ sprintf(the_score.day, "%-.6s %-.2s", ctime(&ct) + 4, ctime(&ct) + 22); #else /* Save the date in standard form (8 chars) */ strftime(the_score.day, 9, "%m/%d/%y", localtime(&ct)); #endif /* Save the player name (15 chars) */ sprintf(the_score.who, "%-.15s", op_ptr->full_name); /* Save the player info XXX XXX XXX */ sprintf(the_score.uid, "%7u", player_uid); sprintf(the_score.sex, "%c", (p_ptr->psex ? 'm' : 'f')); sprintf(the_score.p_r, "%2d", p_ptr->prace); sprintf(the_score.p_c, "%2d", p_ptr->pclass); /* Save the level and such */ sprintf(the_score.cur_lev, "%3d", p_ptr->lev); sprintf(the_score.cur_dun, "%3d", p_ptr->depth); sprintf(the_score.max_lev, "%3d", p_ptr->max_lev); sprintf(the_score.max_dun, "%3d", p_ptr->max_depth); /* Save the cause of death (31 chars) */ sprintf(the_score.how, "%-.31s", p_ptr->died_from); /* Lock (for writing) the highscore file, or fail */ if (fd_lock(highscore_fd, F_WRLCK)) return (1); /* Add a new entry to the score list, see where it went */ j = highscore_add(&the_score); /* Unlock the highscore file, or fail */ if (fd_lock(highscore_fd, F_UNLCK)) return (1); /* Hack -- Display the top fifteen scores */ if (j < 10) { display_scores_aux(0, 15, j, NULL); } /* Display the scores surrounding the player */ else { display_scores_aux(0, 5, j, NULL); display_scores_aux(j - 2, j + 7, j, NULL); } /* Success */ return (0); } /* * Predict the players location, and display it. */ static errr predict_score(void) { int j; high_score the_score; /* No score file */ if (highscore_fd < 0) { msg_print("Score file unavailable."); msg_print(NULL); return (0); } /* Save the version */ sprintf(the_score.what, "%u.%u.%u", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); /* Calculate and save the points */ sprintf(the_score.pts, "%9lu", (long)total_points()); /* Save the current gold */ sprintf(the_score.gold, "%9lu", (long)p_ptr->au); /* Save the current turn */ sprintf(the_score.turns, "%9lu", (long)turn); /* Hack -- no time needed */ strcpy(the_score.day, "TODAY"); /* Save the player name (15 chars) */ sprintf(the_score.who, "%-.15s", op_ptr->full_name); /* Save the player info XXX XXX XXX */ sprintf(the_score.uid, "%7u", player_uid); sprintf(the_score.sex, "%c", (p_ptr->psex ? 'm' : 'f')); sprintf(the_score.p_r, "%2d", p_ptr->prace); sprintf(the_score.p_c, "%2d", p_ptr->pclass); /* Save the level and such */ sprintf(the_score.cur_lev, "%3d", p_ptr->lev); sprintf(the_score.cur_dun, "%3d", p_ptr->depth); sprintf(the_score.max_lev, "%3d", p_ptr->max_lev); sprintf(the_score.max_dun, "%3d", p_ptr->max_depth); /* Hack -- no cause of death */ strcpy(the_score.how, "nobody (yet!)"); /* See where the entry would be placed */ j = highscore_where(&the_score); /* Hack -- Display the top fifteen scores */ if (j < 10) { display_scores_aux(0, 15, j, &the_score); } /* Display some "useful" scores */ else { display_scores_aux(0, 5, -1, NULL); display_scores_aux(j - 2, j + 7, j, &the_score); } /* Success */ return (0); } /* * Change the player into a Winner */ static void kingly(void) { /* Hack -- retire in town */ p_ptr->depth = 0; /* Fake death */ strcpy(p_ptr->died_from, "Ripe Old Age"); /* Restore the experience */ p_ptr->exp = p_ptr->max_exp; /* Restore the level */ p_ptr->lev = p_ptr->max_lev; /* Hack -- Instant Gold */ p_ptr->au += 10000000L; /* Clear screen */ Term_clear(); /* Display a crown */ put_str("#", 1, 34); put_str("#####", 2, 32); put_str("#", 3, 34); put_str(",,, $$$ ,,,", 4, 28); put_str(",,=$ \"$$$$$\" $=,,", 5, 24); put_str(",$$ $$$ $$,", 6, 22); put_str("*> <*> <*", 7, 22); put_str("$$ $$$ $$", 8, 22); put_str("\"$$ $$$ $$\"", 9, 22); put_str("\"$$ $$$ $$\"", 10, 23); put_str("*#########*#########*", 11, 24); put_str("*#########*#########*", 12, 24); /* Display a message */ put_str("Veni, Vidi, Vici!", 15, 26); put_str("I came, I saw, I conquered!", 16, 21); put_str(format("All Hail the Mighty %s!", sp_ptr->winner), 17, 22); /* Flush input */ flush(); /* Wait for response */ pause_line(23); } /* * Close up the current game (player may or may not be dead) * * This function is called only from "main.c" and "signals.c". * * Note that the savefile is not saved until the tombstone is * actually displayed and the player has a chance to examine * the inventory and such. This allows cheating if the game * is equipped with a "quit without save" method. XXX XXX XXX */ void close_game(void) { char buf[1024]; /* Handle stuff */ handle_stuff(); /* Flush the messages */ msg_print(NULL); /* Flush the input */ flush(); /* No suspending now */ signals_ignore_tstp(); /* Hack -- Increase "icky" depth */ character_icky++; /* Build the filename */ path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw"); /* Open the high score file, for reading/writing */ highscore_fd = fd_open(buf, O_RDWR); /* Handle death */ if (p_ptr->is_dead) { /* Handle retirement */ if (p_ptr->total_winner) kingly(); /* You are dead */ print_tomb(); /* Show more info */ show_info(); /* Save dead player */ if (!save_player()) { msg_print("death save failed!"); msg_print(NULL); } #if 0 /* Dump bones file */ make_bones(); #endif /* Handle score, show Top scores */ top_twenty(); } /* Still alive */ else { /* Save the game */ do_cmd_save_game(); /* Prompt for scores XXX XXX XXX */ prt("Press Return (or Escape).", 0, 40); /* Predict score (or ESCAPE) */ if (inkey() != ESCAPE) predict_score(); } /* Shut the high score file */ fd_close(highscore_fd); /* Forget the high score fd */ highscore_fd = -1; /* Hack -- Decrease "icky" depth */ /* character_icky--; */ /* Allow suspending now */ signals_handle_tstp(); } /* * Handle abrupt death of the visual system * * This routine is called only in very rare situations, and only * by certain visual systems, when they experience fatal errors. * * XXX XXX Hack -- clear the death flag when creating a HANGUP * save file so that player can see tombstone when restart. */ void exit_game_panic(void) { /* If nothing important has happened, just quit */ if (!character_generated || character_saved) quit("panic"); /* Mega-Hack -- see "msg_print()" */ msg_flag = FALSE; /* Clear the top line */ prt("", 0, 0); /* Hack -- turn off some things */ disturb(1, 0); /* Hack -- Delay death XXX XXX XXX */ if (p_ptr->chp < 0) p_ptr->is_dead = FALSE; /* Hardcode panic save */ p_ptr->panic_save = 1; /* Forbid suspend */ signals_ignore_tstp(); /* Indicate panic save */ strcpy(p_ptr->died_from, "(panic save)"); /* Panic save, or get worried */ if (!save_player()) quit("panic save failed!"); /* Successful panic save */ quit("panic save succeeded!"); } #ifdef HANDLE_SIGNALS #include /* * Handle signals -- suspend * * Actually suspend the game, and then resume cleanly */ static void handle_signal_suspend(int sig) { /* Disable handler */ (void)signal(sig, SIG_IGN); #ifdef SIGSTOP /* Flush output */ Term_fresh(); /* Suspend the "Term" */ Term_xtra(TERM_XTRA_ALIVE, 0); /* Suspend ourself */ (void)kill(0, SIGSTOP); /* Resume the "Term" */ Term_xtra(TERM_XTRA_ALIVE, 1); /* Redraw the term */ Term_redraw(); /* Flush the term */ Term_fresh(); #endif /* Restore handler */ (void)signal(sig, handle_signal_suspend); } /* * Handle signals -- simple (interrupt and quit) * * This function was causing a *huge* number of problems, so it has * been simplified greatly. We keep a global variable which counts * the number of times the user attempts to kill the process, and * we commit suicide if the user does this a certain number of times. * * We attempt to give "feedback" to the user as he approaches the * suicide thresh-hold, but without penalizing accidental keypresses. * * To prevent messy accidents, we should reset this global variable * whenever the user enters a keypress, or something like that. */ static void handle_signal_simple(int sig) { /* Disable handler */ (void)signal(sig, SIG_IGN); /* Nothing to save, just quit */ if (!character_generated || character_saved) quit(NULL); /* Count the signals */ signal_count++; /* Terminate dead characters */ if (p_ptr->is_dead) { /* Mark the savefile */ strcpy(p_ptr->died_from, "Abortion"); /* Close stuff */ close_game(); /* Quit */ quit("interrupt"); } /* Allow suicide (after 5) */ else if (signal_count >= 5) { /* Cause of "death" */ strcpy(p_ptr->died_from, "Interrupting"); /* Commit suicide */ p_ptr->is_dead = TRUE; /* Stop playing */ p_ptr->playing = FALSE; /* Leaving */ p_ptr->leaving = TRUE; /* Close stuff */ close_game(); /* Quit */ quit("interrupt"); } /* Give warning (after 4) */ else if (signal_count >= 4) { /* Make a noise */ Term_xtra(TERM_XTRA_NOISE, 0); /* Clear the top line */ Term_erase(0, 0, 255); /* Display the cause */ Term_putstr(0, 0, -1, TERM_WHITE, "Contemplating suicide!"); /* Flush */ Term_fresh(); } /* Give warning (after 2) */ else if (signal_count >= 2) { /* Make a noise */ Term_xtra(TERM_XTRA_NOISE, 0); } /* Restore handler */ (void)signal(sig, handle_signal_simple); } /* * Handle signal -- abort, kill, etc */ static void handle_signal_abort(int sig) { /* Disable handler */ (void)signal(sig, SIG_IGN); /* Nothing to save, just quit */ if (!character_generated || character_saved) quit(NULL); /* Clear the bottom line */ Term_erase(0, 23, 255); /* Give a warning */ Term_putstr(0, 23, -1, TERM_RED, "A gruesome software bug LEAPS out at you!"); /* Message */ Term_putstr(45, 23, -1, TERM_RED, "Panic save..."); /* Flush output */ Term_fresh(); /* Panic Save */ p_ptr->panic_save = 1; /* Panic save */ strcpy(p_ptr->died_from, "(panic save)"); /* Forbid suspend */ signals_ignore_tstp(); /* Attempt to save */ if (save_player()) { Term_putstr(45, 23, -1, TERM_RED, "Panic save succeeded!"); } /* Save failed */ else { Term_putstr(45, 23, -1, TERM_RED, "Panic save failed!"); } /* Flush output */ Term_fresh(); /* Quit */ quit("software bug"); } /* * Ignore SIGTSTP signals (keyboard suspend) */ void signals_ignore_tstp(void) { #ifdef SIGTSTP (void)signal(SIGTSTP, SIG_IGN); #endif } /* * Handle SIGTSTP signals (keyboard suspend) */ void signals_handle_tstp(void) { #ifdef SIGTSTP (void)signal(SIGTSTP, handle_signal_suspend); #endif } /* * Prepare to handle the relevant signals */ void signals_init(void) { #ifdef SIGHUP (void)signal(SIGHUP, SIG_IGN); #endif #ifdef SIGTSTP (void)signal(SIGTSTP, handle_signal_suspend); #endif #ifdef SIGINT (void)signal(SIGINT, handle_signal_simple); #endif #ifdef SIGQUIT (void)signal(SIGQUIT, handle_signal_simple); #endif #ifdef SIGFPE (void)signal(SIGFPE, handle_signal_abort); #endif #ifdef SIGILL (void)signal(SIGILL, handle_signal_abort); #endif #ifdef SIGTRAP (void)signal(SIGTRAP, handle_signal_abort); #endif #ifdef SIGIOT (void)signal(SIGIOT, handle_signal_abort); #endif #ifdef SIGKILL (void)signal(SIGKILL, handle_signal_abort); #endif #ifdef SIGBUS (void)signal(SIGBUS, handle_signal_abort); #endif #ifdef SIGSEGV (void)signal(SIGSEGV, handle_signal_abort); #endif #ifdef SIGTERM (void)signal(SIGTERM, handle_signal_abort); #endif #ifdef SIGPIPE (void)signal(SIGPIPE, handle_signal_abort); #endif #ifdef SIGEMT (void)signal(SIGEMT, handle_signal_abort); #endif #ifdef SIGDANGER (void)signal(SIGDANGER, handle_signal_abort); #endif #ifdef SIGSYS (void)signal(SIGSYS, handle_signal_abort); #endif #ifdef SIGXCPU (void)signal(SIGXCPU, handle_signal_abort); #endif #ifdef SIGPWR (void)signal(SIGPWR, handle_signal_abort); #endif } #else /* HANDLE_SIGNALS */ /* * Do nothing */ void signals_ignore_tstp(void) { } /* * Do nothing */ void signals_handle_tstp(void) { } /* * Do nothing */ void signals_init(void) { } #endif /* HANDLE_SIGNALS */