diff options
Diffstat (limited to 'src/files.c')
-rw-r--r-- | src/files.c | 7219 |
1 files changed, 7219 insertions, 0 deletions
diff --git a/src/files.c b/src/files.c new file mode 100644 index 00000000..8c934c8c --- /dev/null +++ b/src/files.c @@ -0,0 +1,7219 @@ +/* File: files.c */ + +/* Purpose: code dealing with files (and death) */ + +/* + * Copyright (c) 1989 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. + */ + +#include "angband.h" + + +static bool setuid_grabbed = TRUE; + + +/* + * You may or may not want to use the following "#undef". + */ +/* #undef _POSIX_SAVED_IDS */ + + +/* + * Hack -- drop permissions + */ +void safe_setuid_drop(void) +{ + if (setuid_grabbed) + { + setuid_grabbed = FALSE; +#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) +{ + if (!setuid_grabbed) + { + setuid_grabbed = TRUE; +#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 /* SAFE_SETUID_POSIX */ + +# endif /* SAFE_SETUID */ + +#endif /* SET_UID */ + } + +} + + +/* + * Extract the first few "tokens" from a buffer + * + * This function uses "colon" and "slash" and delim arg 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, char delim1, char delim2) +{ + 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 == delim1) || (*t == delim2)) 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. + * + * Parse another file recursively, see below for details + * %:<filename> + * + * Specify the attr/char values for "monsters" by race index + * R:<num>:<a>:<c> + * + * Specify the attr/char values for "objects" by kind index + * K:<num>:<a>:<c> + * + * Specify the attr/char values for "features" by feature index + * F:<num>:<a>:<c> + * + * Specify the attr/char values for "stores" by store index + * B:<num>:<a>:<c> + * + * Specify the attr/char values for unaware "objects" by kind tval + * U:<tv>:<a>:<c> + * + * Specify the attr/char values for inventory "objects" by kind tval + * E:<tv>:<a>:<c> + * + * Define a macro action, given an encoded macro action + * A:<str> + * + * Create a normal macro, given an encoded macro trigger + * P:<str> + * + * Create a command macro, given an encoded macro trigger + * C:<str> + * + * Create a keyset mapping + * S:<key>:<key>:<dir> + * + * Turn an option off, given its name + * X:<str> + * + * Turn an option on, given its name + * Y:<str> + * + * Specify visual information, given an index, and some data + * V:<num>:<kv>:<rv>:<gv>:<bv> + * + * Specify squelch settings + * Q:<num>:<squelch> + */ +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); + + /* Require "?:*" format */ + if (buf[1] != ':') return (1); + + + /* Process "%:<fname>" */ + if (buf[0] == '%') + { + /* Attempt to Process the given file */ + return (process_pref_file(buf + 2)); + } + + + /* Process "R:<num>:<a>/<c>" -- 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 "G:<type>:<num>:<a>/<c>" -- attr/char for overlay graphics */ + if (buf[0] == 'G') + { + /* Process "G:M:<num>:<a>/<c>" -- attr/char for ego monsters */ + if (buf[2] == 'M') + { + if (tokenize(buf + 4, 3, zz, ':', '/') == 3) + { + monster_ego *re_ptr; + i = (huge)strtol(zz[0], NULL, 0); + n1 = strtol(zz[1], NULL, 0); + n2 = strtol(zz[2], NULL, 0); + if (i >= max_re_idx) return (1); + re_ptr = &re_info[i]; + if (n1) re_ptr->g_attr = n1; + if (n2) + { + re_ptr->g_char = n2; + } + return (0); + } + } + + /* Process "G:P:<num>:<a>/<c>" -- attr/char for race modifiers */ + if (buf[2] == 'P') + { + if (tokenize(buf + 4, 3, zz, ':', '/') == 3) + { + player_race_mod *rmp_ptr; + i = (huge)strtol(zz[0], NULL, 0); + n1 = strtol(zz[1], NULL, 0); + n2 = strtol(zz[2], NULL, 0); + if (i >= max_rmp_idx) return (1); + rmp_ptr = &race_mod_info[i]; + if (n1) rmp_ptr->g_attr = n1; + if (n2) + { + rmp_ptr->g_char = n2; + } + return (0); + } + } + + /* Process "G:T:<num>:<a>/<c>" -- attr/char for traps */ + if (buf[2] == 'T') + { + if (tokenize(buf + 4, 3, zz, ':', '/') == 3) + { + trap_type *t_ptr; + i = (huge)strtol(zz[0], NULL, 0); + n1 = strtol(zz[1], NULL, 0); + n2 = strtol(zz[2], NULL, 0); + if (i >= max_t_idx) return (1); + t_ptr = &t_info[i]; + if (n1) t_ptr->g_attr = n1; + if (n2) + { + t_ptr->g_char = n2; + } + return (0); + } + } + } + + + /* Process "K:<num>:<a>/<c>" -- 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:<num>:<a>/<c>" -- 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 "B:<num>:<a>/<c>" -- attr/char for stores */ + else if (buf[0] == 'B') + { + if (tokenize(buf + 2, 3, zz, ':', '/') == 3) + { + store_info_type *st_ptr; + i = (huge)strtol(zz[0], NULL, 0); + n1 = strtol(zz[1], NULL, 0); + n2 = strtol(zz[2], NULL, 0); + if (i >= max_st_idx) return (1); + st_ptr = &st_info[i]; + if (n1) st_ptr->x_attr = n1; + if (n2) st_ptr->x_char = n2; + return (0); + } + } + + /* Process "S:<num>:<a>/<c>" -- 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 "U:<tv>:<a>/<c>" -- attr/char for unaware items */ + else if (buf[0] == 'U') + { + if (tokenize(buf + 2, 3, zz, ':', '/') == 3) + { + j = (huge)strtol(zz[0], NULL, 0); + n1 = strtol(zz[1], NULL, 0); + n2 = strtol(zz[2], NULL, 0); + for (i = 1; i < max_k_idx; i++) + { + object_kind *k_ptr = &k_info[i]; + if (k_ptr->tval == j) + { + if (n1) k_ptr->d_attr = n1; + if (n2) k_ptr->d_char = n2; + } + } + return (0); + } + } + + + /* Process "E:<tv>:<a>" -- 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:<str>" -- save an "action" for later */ + else if (buf[0] == 'A') + { + text_to_ascii(macro__buf, buf + 2); + return (0); + } + + /* Process "P:<str>" -- normal macro */ + else if (buf[0] == 'P') + { + char tmp[1024]; + text_to_ascii(tmp, buf + 2); + macro_add(tmp, macro__buf); + return (0); + } + + /* Process "L:<num>:<trigger>:<descr> -- extended command macro */ + else if (buf[0] == 'L') + { + switch (tokenize(buf + 2, 3, zz, ':', 0)) + { + case 3: + cli_add(zz[0], zz[1], zz[2]); + return 0; + case 2: + cli_add(zz[0], zz[1], 0); + return 0; + default: + return 1; + } + } + + /* Process "C:<str>" -- 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__buf); + + return (0); + } + + + /* Process "V:<num>:<kv>:<rv>:<gv>:<bv>" -- 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); + } + } + /* set macro trigger names and a template */ + /* Process "T:<trigger>:<keycode>:<shift-keycode>" */ + /* Process "T:<template>:<modifier chr>:<modifier name>:..." */ + else if (buf[0] == 'T') + { + int len, tok; + tok = tokenize(buf + 2, 2 + MAX_MACRO_MOD, zz, ':', '/'); + if (tok >= 4) + { + int i; + int num; + + if (macro_template != NULL) + { + free(macro_template); + macro_template = NULL; + for (i = 0; i < max_macrotrigger; i++) + free(macro_trigger_name[i]); + max_macrotrigger = 0; + } + + if (*zz[0] == '\0') return 0; /* clear template */ + num = strlen(zz[1]); + if (2 + num != tok) return 1; /* error */ + + len = strlen(zz[0]) + 1 + num + 1; + for (i = 0; i < num; i++) + len += strlen(zz[2 + i]) + 1; + macro_template = malloc(len); + + strcpy(macro_template, zz[0]); + macro_modifier_chr = + macro_template + strlen(macro_template) + 1; + strcpy(macro_modifier_chr, zz[1]); + macro_modifier_name[0] = + macro_modifier_chr + strlen(macro_modifier_chr) + 1; + for (i = 0; i < num; i++) + { + strcpy(macro_modifier_name[i], zz[2 + i]); + macro_modifier_name[i + 1] = macro_modifier_name[i] + + strlen(macro_modifier_name[i]) + 1; + } + } + else if (tok >= 2) + { + int m; + char *t, *s; + if (max_macrotrigger >= MAX_MACRO_TRIG) + { + msg_print("Too many macro triggers!"); + return 1; + } + m = max_macrotrigger; + max_macrotrigger++; + + len = strlen(zz[0]) + 1 + strlen(zz[1]) + 1; + if (tok == 3) + len += strlen(zz[2]) + 1; + macro_trigger_name[m] = malloc(len); + + t = macro_trigger_name[m]; + s = zz[0]; + while (*s) + { + if ('\\' == *s) s++; + *t++ = *s++; + } + *t = '\0'; + + macro_trigger_keycode[0][m] = macro_trigger_name[m] + + strlen(macro_trigger_name[m]) + 1; + strcpy(macro_trigger_keycode[0][m], zz[1]); + if (tok == 3) + { + macro_trigger_keycode[1][m] = macro_trigger_keycode[0][m] + + strlen(macro_trigger_keycode[0][m]) + 1; + strcpy(macro_trigger_keycode[1][m], zz[2]); + } + else + { + macro_trigger_keycode[1][m] = macro_trigger_keycode[0][m]; + } + } + return 0; + } + + /* Process "X:<str>" -- turn option off */ + else if (buf[0] == 'X') + { + for (i = 0; option_info[i].o_desc; i++) + { + if (option_info[i].o_var && + option_info[i].o_text && + streq(option_info[i].o_text, buf + 2)) + { + (*option_info[i].o_var) = FALSE; + return (0); + } + } + } + + /* Process "Y:<str>" -- turn option on */ + else if (buf[0] == 'Y') + { + for (i = 0; option_info[i].o_desc; i++) + { + if (option_info[i].o_var && + option_info[i].o_text && + streq(option_info[i].o_text, buf + 2)) + { + (*option_info[i].o_var) = TRUE; + return (0); + } + } + } + + /* Process "W:<win>:<flag>:<value>" -- 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 >= ANGBAND_TERM_MAX)) 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 */ + window_flag[win] |= (1L << flag); + } + else + { + /* Turn flag off */ + window_flag[win] &= ~(1L << flag); + } + } + + /* Success */ + return (0); + } + } + + /* Process "Q:<num>:<squelch>" -- item squelch flags */ + else if (buf[0] == 'Q') + { + /* This option isn't used anymore */ + 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"; + } + } + + /* Function: LEQN */ + else if (streq(t, "LEQN")) + { + int n = 0; + v = "1"; + if (*s && (f != b2)) + { + t = process_pref_file_expr(&s, &f); + n = atoi(t); + } + while (*s && (f != b2)) + { + p = t; + t = process_pref_file_expr(&s, &f); + if (*t && (atoi(t) < n)) v = "0"; + } + } + + /* Function: GEQN */ + else if (streq(t, "GEQN")) + { + int n = 0; + v = "1"; + if (*s && (f != b2)) + { + t = process_pref_file_expr(&s, &f); + n = atoi(t); + } + while (*s && (f != b2)) + { + p = t; + t = process_pref_file_expr(&s, &f); + if (*t && (atoi(t) > n)) v = "0"; + } + } + + /* Function SKILL */ + else if (streq(t, "SKILL")) + { + static char skill_val[4*sizeof(int) + 1]; + s16b skill = -1; + v = "0"; + while (*s && (f != b2)) + { + t = process_pref_file_expr(&s, &f); + if (*t) skill = find_skill_i(t); + } + if (skill > 0) + { + sprintf(skill_val, "%d", (int)get_skill(skill)); + v = skill_val; + } + } + + /* 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; + } + + else if (streq(b + 1, "KEYBOARD")) + { + v = ANGBAND_KEYBOARD; + } + + /* Graphics */ + if (streq(b + 1, "GRAF")) + { + v = ANGBAND_GRAF; + } + + /* Race */ + else if (streq(b + 1, "RACE")) + { + v = rp_ptr->title + rp_name; + } + + /* Race */ + else if (streq(b + 1, "RACEMOD")) + { + v = rmp_ptr->title + rmp_name; + } + + /* Class */ + else if (streq(b + 1, "CLASS")) + { + v = spp_ptr->title + c_name; + } + + /* Player */ + else if (streq(b + 1, "PLAYER")) + { + v = player_base; + } + } + + /* 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]; + + int num = -1; + + errr err = 0; + + bool bypass = FALSE; + + /* Build the filename -- Allow users to override system pref files */ + path_build(buf, 1024, ANGBAND_DIR_USER, name); + + /* Open the file */ + fp = my_fopen(buf, "r"); + + /* No such file -- Try system pref file */ + if (!fp) + { + /* Build the pathname, this time using the system pref directory */ + path_build(buf, 1024, ANGBAND_DIR_PREF, name); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the file */ + fp = my_fopen(buf, "r"); + + /* Drop permission */ + safe_setuid_drop(); + + /* Failed again */ + 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; + + + /* Process "?:<expr>" */ + 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 "%:<file>" */ + 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'", buf); + } + + /* 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); +} + + + +/* + * !Ran under the game's permission! + * + * 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"); + + /* + * XXX No need to grab permission here because this function is called + * only once before the game drops "game" permission + */ + + /* 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); +} + + +/* + * !Ran under the game's permission! + * + * 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"); + + /* + * XXX No need to grab permission here because this function is called + * only once before the game drops "game" permission + */ + + /* 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); +} + + +/* + * Print long number with header at given row, column + * Use the color for the number, not the header + */ +static void prt_lnum(cptr header, s32b num, int row, int col, byte color) +{ + int len = strlen(header); + char out_val[32]; + + put_str(header, row, col); + (void)sprintf(out_val, "%9ld", (long)num); + c_put_str(color, out_val, row, col + len); +} + + +/* + * Print number with header at given row, column + */ +static void prt_num(cptr header, int num, int row, int col, byte color, + char *space) +{ + int len = strlen(header); + char out_val[32]; + + put_str(header, row, col); + put_str(space, row, col + len); + (void)sprintf(out_val, "%6ld", (long)num); + c_put_str(color, out_val, row, col + len + strlen(space)); +} + + +/* + * Print str with header at given row, column + */ +static void prt_str(cptr header, cptr str, int row, int col, byte color) +{ + int len = strlen(header); + char out_val[32]; + + put_str(header, row, col); + put_str(" ", row, col + len); + (void)sprintf(out_val, "%6s", str); + c_put_str(color, out_val, row, col + len + 3); +} + + +/* + * Prints the following information on the screen. + * + * For this to look right, the following should be spaced the + * same as in the prt_lnum code... -CFT + */ +static void display_player_middle(void) +{ + int show_tohit = p_ptr->dis_to_h; + int show_todam = p_ptr->dis_to_d; + + object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD]; + char num[7]; + byte color; + int speed; + + + /* Hack -- add in weapon info if known */ + if (object_known_p(o_ptr)) show_tohit = p_ptr->dis_to_h + p_ptr->to_h_melee + o_ptr->to_h; + else show_tohit = p_ptr->dis_to_h + p_ptr->to_h_melee; + if (object_known_p(o_ptr)) show_todam = p_ptr->dis_to_d + p_ptr->to_d_melee + o_ptr->to_d; + else show_todam = p_ptr->dis_to_d + p_ptr->to_d_melee; + + /* Dump the bonuses to hit/dam */ + prt_num("+ To Melee Hit ", show_tohit, 9, 1, TERM_L_BLUE, " "); + prt_num("+ To Melee Damage", show_todam, 10, 1, TERM_L_BLUE, " "); + + o_ptr = &p_ptr->inventory[INVEN_BOW]; + + /* Hack -- add in weapon info if known */ + if (object_known_p(o_ptr)) show_tohit = p_ptr->dis_to_h + p_ptr->to_h_ranged + o_ptr->to_h; + else show_tohit = p_ptr->dis_to_h + p_ptr->to_h_ranged; + if (object_known_p(o_ptr)) show_todam = p_ptr->to_d_ranged + o_ptr->to_d; + else show_todam = p_ptr->to_d_ranged; + + prt_num("+ To Ranged Hit ", show_tohit, 11, 1, TERM_L_BLUE, " "); + prt_num("+ To Ranged Damage", show_todam, 12, 1, TERM_L_BLUE, " "); + + /* Dump the total armor class */ + prt_str(" AC ", format("%d+%d", p_ptr->ac, p_ptr->dis_to_a), 13, 1, TERM_L_BLUE); + + prt_num("Level ", (int)p_ptr->lev, 9, 28, TERM_L_GREEN, " "); + + if (p_ptr->exp >= p_ptr->max_exp) + { + prt_lnum("Experience ", p_ptr->exp, 10, 28, TERM_L_GREEN); + } + else + { + prt_lnum("Experience ", p_ptr->exp, 10, 28, TERM_YELLOW); + } + + prt_lnum("Max Exp ", p_ptr->max_exp, 11, 28, TERM_L_GREEN); + + if ((p_ptr->lev >= PY_MAX_LEVEL) || (p_ptr->lev >= max_plev)) + { + put_str("Exp to Adv.", 12, 28); + c_put_str(TERM_L_GREEN, " *****", 12, 28 + 11); + } + else + { + prt_lnum("Exp to Adv.", + (s32b)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L), + 12, 28, TERM_L_GREEN); + } + + prt_lnum("Gold ", p_ptr->au, 13, 28, TERM_L_GREEN); + + if (p_ptr->necro_extra & CLASS_UNDEAD) + { + put_str("Death Points ", 9, 52); + if (p_ptr->chp >= p_ptr->mhp) + { + color = TERM_L_BLUE; + } + else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10) + { + color = TERM_VIOLET; + } + else + { + color = TERM_L_RED; + } + (void)sprintf(num, "%6ld", (long)p_ptr->chp); + c_put_str(color, num, 9, 65); + put_str("/", 9, 71); + (void)sprintf(num, "%6ld", (long)p_ptr->mhp); + c_put_str(TERM_L_BLUE, num, 9, 72); + } + else + { + put_str("Hit Points ", 9, 52); + if (p_ptr->chp >= p_ptr->mhp) + { + color = TERM_L_GREEN; + } + else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10) + { + color = TERM_YELLOW; + } + else + { + color = TERM_RED; + } + (void)sprintf(num, "%6ld", (long)p_ptr->chp); + c_put_str(color, num, 9, 65); + put_str("/", 9, 71); + (void)sprintf(num, "%6ld", (long)p_ptr->mhp); + c_put_str(TERM_L_GREEN, num, 9, 72); + } + + put_str("Spell Points ", 10, 52); + if (p_ptr->csp >= p_ptr->msp) + { + color = TERM_L_GREEN; + } + else if (p_ptr->csp > (p_ptr->msp * hitpoint_warn) / 10) + { + color = TERM_YELLOW; + } + else + { + color = TERM_RED; + } + (void)sprintf(num, "%6ld", (long)p_ptr->csp); + c_put_str(color, num, 10, 65); + put_str("/", 10, 71); + (void)sprintf(num, "%6ld", (long)p_ptr->msp); + c_put_str(TERM_L_GREEN, num, 10, 72); + + put_str("Sanity ", 11, 52); + if (p_ptr->csane >= p_ptr->msane) + { + color = TERM_L_GREEN; + } + else if (p_ptr->csane > (p_ptr->msane * hitpoint_warn) / 10) + { + color = TERM_YELLOW; + } + else + { + color = TERM_RED; + } + (void)sprintf(num, "%6ld", (long)p_ptr->csane); + c_put_str(color, num, 11, 65); + put_str("/", 11, 71); + (void)sprintf(num, "%6ld", (long)p_ptr->msane); + c_put_str(TERM_L_GREEN, num, 11, 72); + + if (p_ptr->pgod != GOD_NONE) + { + prt_num("Piety ", p_ptr->grace, 12, 52, TERM_L_GREEN, " "); + } + + put_str("Speed ", 13, 52); + speed = p_ptr->pspeed; + /* Hack -- Visually "undo" the Search Mode Slowdown */ + if (p_ptr->searching) speed += 10; + if (speed > 110) + { + char s[11]; + (void)sprintf(s, "Fast (+%d)", speed - 110); + c_put_str(TERM_L_GREEN, s, 13, (speed >= 120) ? 68 : 69); + } + else if (speed < 110) + { + char s[11]; + (void)sprintf(s, "Slow (-%d)", 110 - speed); + c_put_str(TERM_L_UMBER, s, 13, (speed <= 100) ? 68 : 69); + } + else + { + put_str("Normal", 13, 72); + } +} + + + + +/* + * 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) +{ + static char dummy[20] = ""; + + /* Paranoia */ + if (y <= 0) y = 1; + + /* Negative value */ + if (x < 0) + { + likert_color = TERM_L_DARK; + return ("Very Bad"); + } + + /* Analyze the value */ + switch ((x / y)) + { + case 0: + case 1: + { + likert_color = TERM_RED; + return ("Bad"); + } + case 2: + { + likert_color = TERM_L_RED; + return ("Poor"); + } + case 3: + case 4: + { + likert_color = TERM_ORANGE; + 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_GREEN; + return ("Superb"); + } + case 14: + case 15: + case 16: + case 17: + { + likert_color = TERM_L_GREEN; + return ("Heroic"); + } + default: + { + likert_color = TERM_L_GREEN; + sprintf(dummy, "Legendary[%d]", (int)((((x / y) - 17) * 5) / 2)); + return dummy; + } + } +} + + +/* + * Prints ratings on certain abilities + * + * This code is "imitated" elsewhere to "dump" a character sheet. + */ +static void display_player_various(void) +{ + int tmp, tmp2, damdice, damsides, dambonus, blows; + int xthn, xthb, xfos, xsrh; + int xdis, xdev, xsav, xstl; + cptr desc; + int i; + + object_type *o_ptr; + + + /* Fighting Skill (with current weapon) */ + o_ptr = &p_ptr->inventory[INVEN_WIELD]; + tmp = p_ptr->to_h + o_ptr->to_h + p_ptr->to_h_melee; + xthn = p_ptr->skill_thn + (tmp * BTH_PLUS_ADJ); + + /* Shooting Skill (with current bow and normal missile) */ + o_ptr = &p_ptr->inventory[INVEN_BOW]; + tmp = p_ptr->to_h + o_ptr->to_h + p_ptr->to_h_ranged; + xthb = p_ptr->skill_thb + (tmp * BTH_PLUS_ADJ); + + /* variables for all types of melee damage */ + dambonus = p_ptr->dis_to_d; + blows = p_ptr->num_blow; + + /* 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("Fighting :", 16, 1); + desc = likert(xthn, 12); + c_put_str(likert_color, desc, 16, 15); + + put_str("Bows/Throw :", 17, 1); + desc = likert(xthb, 12); + c_put_str(likert_color, desc, 17, 15); + + put_str("Saving Throw:", 18, 1); + desc = likert(xsav, 6); + c_put_str(likert_color, desc, 18, 15); + + put_str("Stealth :", 19, 1); + desc = likert(xstl, 1); + c_put_str(likert_color, desc, 19, 15); + + + put_str("Perception :", 16, 28); + desc = likert(xfos, 6); + c_put_str(likert_color, desc, 16, 42); + + put_str("Searching :", 17, 28); + desc = likert(xsrh, 6); + c_put_str(likert_color, desc, 17, 42); + + put_str("Disarming :", 18, 28); + desc = likert(xdis, 8); + c_put_str(likert_color, desc, 18, 42); + + put_str("Magic Device:", 19, 28); + desc = likert(xdev, 6); + c_put_str(likert_color, desc, 19, 42); + + + put_str("Blows/Round:", 16, 55); + put_str(format("%d", p_ptr->num_blow), 16, 69); + + put_str("Shots/Round:", 17, 55); + put_str(format("%d", p_ptr->num_fire), 17, 69); + + put_str("Mel.dmg/Rnd:", 18, 55); /* From PsiAngband */ + + if (p_ptr->melee_style == SKILL_HAND || p_ptr->melee_style == SKILL_BEAR) + { + /* This is all based on py_attack_hand */ + martial_arts *blow_table, *min_attack, *max_attack; + int max_blow, plev, i; + + if (p_ptr->melee_style == SKILL_HAND) + { + blow_table = ma_blows; + max_blow = MAX_MA; + plev = get_skill(SKILL_HAND); + } + else /* SKILL_BEAR */ + { + blow_table = bear_blows; + max_blow = MAX_BEAR; + plev = get_skill(SKILL_BEAR); + } + min_attack = blow_table; + i = max_blow - 1; + while (blow_table[i].min_level > plev && i != 0) + --i; + max_attack = &blow_table[i]; + + dambonus += p_ptr->to_d_melee; + tmp = min_attack->dd + dambonus; + if (tmp < 0) tmp = 0; + tmp2 = maxroll(max_attack->dd, max_attack->ds) + dambonus; + if (tmp2 < 0) tmp2 = 0; + if (!tmp && !tmp2) + desc = "0"; + else + desc = format("%d-%d", blows * tmp, blows * tmp2); + } + else if (!r_info[p_ptr->body_monster].body_parts[BODY_WEAPON]) + { + if (r_info[p_ptr->body_monster].flags1 & RF1_NEVER_BLOW) + desc = "nil!"; + else + { + tmp = tmp2 = 0; + for (i = 0; i < blows; i++) + { + tmp += r_info[p_ptr->body_monster].blow[i].d_dice; + tmp2 += maxroll(r_info[p_ptr->body_monster].blow[i].d_dice, + r_info[p_ptr->body_monster].blow[i].d_side); + } + if (dambonus > 0) + { + tmp += dambonus; + tmp2 += dambonus; + } + desc = format("%d-%d", tmp, tmp2); + } + } + else + { + /* Increase the bonus to damage for weapon combat */ + dambonus += p_ptr->to_d_melee; + + /* Access the first weapon */ + o_ptr = &p_ptr->inventory[INVEN_WIELD]; + + if (object_known_p(o_ptr)) dambonus += o_ptr->to_d; + + damdice = o_ptr->dd; + damsides = o_ptr->ds; + + if ((damdice == 0) || (damsides == 0)) + { + if (dambonus <= 0) + desc = "nil!"; + else + desc = format("%d", blows * dambonus); + } + else + { + if (dambonus == 0) + desc = format("%dd%d", blows * damdice, damsides); + else + desc = format("%dd%d%c%d", blows * damdice, damsides, + ( dambonus > 0 ? '+' : '\0' ), blows * dambonus ); + } + } + put_str(desc, 18, 69); + + + put_str("Infra-Vision:", 19, 55); + put_str(format("%d feet", p_ptr->see_infra * 10), 19, 69); + + /* jk - add tactic */ + put_str("Tactic:", 20, 55); + c_put_str(TERM_L_BLUE, tactic_info[(byte)p_ptr->tactic].name, 20, 69); + + /* jk - add movement */ + put_str("Explor:", 21, 55); + c_put_str(TERM_L_BLUE, move_info[(byte)p_ptr->movement].name, 21, 69); +} + + + +/* + * Obtain the "flags" of the wielded symbiote + */ + +void wield_monster_flags(u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp) +{ + object_type *o_ptr; + monster_race *r_ptr; + + /* Clear */ + (*f1) = (*f2) = (*f3) = (*f4) = (*f5) = (*esp) = 0L; + + /* Get the carried monster */ + o_ptr = &p_ptr->inventory[INVEN_CARRY]; + + if (o_ptr->k_idx) + { + r_ptr = &r_info[o_ptr->pval]; + + if (r_ptr->flags2 & RF2_INVISIBLE) + (*f2) |= TR2_INVIS; + if (r_ptr->flags2 & RF2_REFLECTING) + (*f2) |= TR2_REFLECT; + if (r_ptr->flags7 & RF7_CAN_FLY) + (*f3) |= TR3_FEATHER; + if (r_ptr->flags7 & RF7_AQUATIC) + (*f5) |= TR5_WATER_BREATH; + } +} + + +/* + * Obtain the "flags" for the player as if he was an item + */ +void player_flags(u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp) +{ + int i; + + /* Clear */ + (*f1) = (*f2) = (*f3) = (*f4) = (*f5) = (*esp) = 0L; + + /* Astral chars */ + if (p_ptr->astral) + { + (*f3) |= TR3_WRAITH; + } + +/* Skills */ + if (get_skill(SKILL_DAEMON) > 20) (*f2) |= TR2_RES_CONF; + if (get_skill(SKILL_DAEMON) > 30) (*f2) |= TR2_RES_FEAR; + if (get_skill(SKILL_MINDCRAFT) >= 40) (*esp) |= ESP_ALL; + if (p_ptr->melee_style == SKILL_HAND && get_skill(SKILL_HAND) > 24 && !monk_heavy_armor()) + (*f2) |= TR2_FREE_ACT; +/* Hack - from Lua */ + if (get_skill(SKILL_MANA) >= 35) (*f1) |= TR1_MANA; + if (get_skill(SKILL_AIR) >= 50) (*f5) |= (TR5_MAGIC_BREATH | TR5_WATER_BREATH); + if (get_skill(SKILL_WATER) >= 30) (*f5) |= TR5_WATER_BREATH; + +/* Gods */ + GOD(GOD_ERU) + { + if ((p_ptr->grace >= 100) || (p_ptr->grace <= -100)) (*f1) |= TR1_MANA; + if (p_ptr->grace > 10000) (*f1) |= TR1_WIS; + } + + GOD(GOD_MELKOR) + { + (*f2) |= TR2_RES_FIRE; + if (p_ptr->melkor_sacrifice > 0) (*f2) |= TR2_LIFE; + if (p_ptr->grace > 10000) (*f1) |= (TR1_STR | TR1_CON | TR1_INT | TR1_WIS | TR1_CHR); + PRAY_GOD(GOD_MELKOR) + { + if (p_ptr->grace > 5000) (*f2) |= TR2_INVIS; + if (p_ptr->grace > 15000) (*f2) |= TR2_IM_FIRE; + } + } + + GOD(GOD_MANWE) + { + if (p_ptr->grace >= 2000) (*f3) |= TR3_FEATHER; + PRAY_GOD(GOD_MANWE) + { + if (p_ptr->grace >= 7000) (*f2) |= TR2_FREE_ACT; + if (p_ptr->grace >= 15000) (*f4) |= TR4_FLY; + if ((p_ptr->grace >= 5000) || (p_ptr->grace <= -5000)) (*f1) |= TR1_SPEED; + } + } + + GOD(GOD_TULKAS) + { + if (p_ptr->grace > 5000) (*f1) |= TR1_CON; + if (p_ptr->grace > 10000) (*f1) |= TR1_STR; + } + + /* Classes */ + for (i = 1; i <= p_ptr->lev; i++) + { + (*f1) |= cp_ptr->oflags1[i]; + (*f2) |= cp_ptr->oflags2[i]; + (*f3) |= cp_ptr->oflags3[i]; + (*f4) |= cp_ptr->oflags4[i]; + (*f5) |= cp_ptr->oflags5[i]; + (*esp) |= cp_ptr->oesp[i]; + } + + /* Races */ + if ((!p_ptr->mimic_form) && (!p_ptr->body_monster)) + { + for (i = 1; i <= p_ptr->lev; i++) + { + (*f1) |= rp_ptr->oflags1[i]; + (*f2) |= rp_ptr->oflags2[i]; + (*f3) |= rp_ptr->oflags3[i]; + (*f4) |= rp_ptr->oflags4[i]; + (*f5) |= rp_ptr->oflags5[i]; + (*esp) |= rp_ptr->oesp[i]; + + (*f1) |= rmp_ptr->oflags1[i]; + (*f2) |= rmp_ptr->oflags2[i]; + (*f3) |= rmp_ptr->oflags3[i]; + (*f4) |= rmp_ptr->oflags4[i]; + (*f5) |= rmp_ptr->oflags5[i]; + (*esp) |= rmp_ptr->oesp[i]; + } + } +#if 0 /* DGDGDG? */ + else if (p_ptr->mimic_form) + { + switch (p_ptr->mimic_form) + { + case MIMIC_GOAT: + { + (*f3) |= (TR3_SLOW_DIGEST); + break; + } + case MIMIC_INSECT: + { + (*esp) |= (ESP_ANIMAL); + break; + } + case MIMIC_SPARROW: + { + (*f3) |= (TR3_FEATHER); + break; + } + case MIMIC_VAMPIRE: + { + (*f3) |= (TR2_HOLD_LIFE); + (*f3) |= (TR2_RES_DARK); + (*f3) |= (TR2_RES_NEXUS); + (*f3) |= (TR2_RES_BLIND); + (*f3) |= (TR3_LITE1); + break; + } + case MIMIC_SPIDER: + { + (*f3) |= (TR2_RES_FEAR); + (*f3) |= (TR2_RES_POIS); + break; + } + case MIMIC_MANA_BALL: + { + (*f3) |= (TR3_FEATHER); + (*f2) |= (TR2_INVIS); + (*f3) |= (TR3_TELEPORT); + (*f3) |= (TR2_RES_DISEN); + break; + } + case MIMIC_FIRE_CLOUD: + { + (*f3) |= (TR2_RES_LITE); + (*f3) |= (TR2_IM_FIRE); + (*f3) |= (TR3_SH_FIRE); + break; + } + case MIMIC_COLD_CLOUD: + { + (*f3) |= (TR2_RES_LITE); + (*f3) |= (TR2_IM_COLD); + (*f3) |= (TR3_SH_ELEC); + (*esp) |= (ESP_EVIL); + (*f3) |= (TR3_REGEN); + break; + } + case MIMIC_CHAOS_CLOUD: + { + (*f3) |= (TR2_RES_DISEN); + (*f3) |= (TR2_RES_CHAOS); + (*f3) |= (TR2_RES_LITE); + (*f3) |= (TR2_IM_FIRE); + (*f3) |= (TR2_IM_COLD); + (*f3) |= (TR3_SH_FIRE); + (*f3) |= (TR3_SH_ELEC); + break; + } + case MIMIC_GOST: + { + (*f3) |= (TR3_WRAITH); + (*f2) |= TR2_HOLD_LIFE; + break; + } + case MIMIC_ENT: + { + (*f2) |= TR2_SENS_FIRE; + break; + } + case MIMIC_KOBOLD: + { + (*f2) |= TR2_RES_POIS; + break; + } + case MIMIC_DRAGON: + { + (*f3) |= TR3_FEATHER; + (*f2) |= TR2_RES_FIRE; + (*f2) |= TR2_RES_COLD; + (*f2) |= TR2_RES_ELEC; + (*f2) |= TR2_RES_DARK; + break; + } + case MIMIC_DEMON: + { + (*f2) |= TR2_RES_CHAOS; + (*f2) |= TR2_RES_NETHER; + (*f2) |= TR2_HOLD_LIFE; + break; + } + case MIMIC_HOUND: + { + (*f1) |= TR1_SPEED; + (*f2) |= TR2_RES_LITE; + (*f2) |= TR2_RES_DARK; + break; + } + case MIMIC_QUYLTHULG: + { + (*f3) |= TR3_SEE_INVIS; + break; + } + case MIMIC_MAIAR: + { + (*f2) |= TR2_IM_ACID; + (*f2) |= TR2_IM_ELEC; + (*f2) |= TR2_IM_FIRE; + (*f2) |= TR2_IM_COLD; + (*f2) |= TR2_RES_POIS; + (*f2) |= TR2_RES_LITE; + (*f2) |= TR2_RES_DARK; + (*f2) |= TR2_RES_CHAOS; + (*f2) |= TR2_HOLD_LIFE; + (*f3) |= TR3_FEATHER; + (*f3) |= TR3_REGEN; + break; + } + case MIMIC_SERPENT: + { + (*f1) |= TR1_SPEED; + break; + } + case MIMIC_GIANT: + { + (*f2) |= TR2_RES_ACID; + (*f2) |= TR2_RES_ELEC; + (*f2) |= TR2_RES_FIRE; + (*f2) |= TR2_RES_COLD; + (*f2) |= TR2_RES_POIS; + (*f2) |= TR2_RES_CONF; + (*f2) |= TR2_RES_SOUND; + (*f2) |= TR2_RES_LITE; + (*f2) |= TR2_RES_DARK; + (*f2) |= TR2_RES_NEXUS; + (*f2) |= TR2_RES_FEAR; + (*f2) |= TR2_REFLECT; + break; + } + case MIMIC_VALAR: + { + (*f3) |= TR3_SEE_INVIS; + (*f2) |= TR2_FREE_ACT; + (*f3) |= TR3_SLOW_DIGEST; + (*f3) |= TR3_REGEN; + (*f3) |= TR3_FEATHER; + (*f2) |= TR2_HOLD_LIFE; + (*esp) |= ESP_ALL; + (*f3) |= TR3_LITE1; + (*f2) |= TR2_SUST_STR; + (*f1) |= TR1_INT; + (*f1) |= TR1_WIS; + (*f1) |= TR1_DEX; + (*f1) |= TR1_CON; + (*f1) |= TR1_CHR; + (*f2) |= TR2_RES_ACID; + (*f2) |= TR2_RES_ELEC; + (*f2) |= TR2_RES_FIRE; + (*f2) |= TR2_RES_COLD; + (*f2) |= TR2_RES_POIS; + (*f2) |= TR2_RES_CONF; + (*f2) |= TR2_RES_SOUND; + (*f2) |= TR2_RES_LITE; + (*f2) |= TR2_RES_DARK; + (*f2) |= TR2_RES_CHAOS; + (*f2) |= TR2_RES_DISEN; + (*f2) |= TR2_RES_SHARDS; + (*f2) |= TR2_RES_NEXUS; + (*f2) |= TR2_RES_BLIND; + (*f2) |= TR2_RES_NETHER; + (*f2) |= TR2_RES_FEAR; + (*f2) |= TR2_REFLECT; + (*f3) |= TR3_SH_FIRE; + (*f3) |= TR3_SH_ELEC; + (*f2) |= TR2_IM_FIRE; + (*f2) |= TR2_IM_COLD; + (*f2) |= TR2_IM_ELEC; + (*f2) |= TR2_IM_ACID; + break; + } + + case MIMIC_WEREWOLF: + { + (*f3) |= TR3_REGEN; + (*f3) |= TR3_AGGRAVATE; + break; + } + case MIMIC_BALROG: + { + (*f2) |= TR2_IM_ACID; + (*f2) |= TR2_IM_ELEC; + (*f2) |= TR2_IM_FIRE; + (*f2) |= TR2_RES_POIS; + (*f2) |= TR2_RES_DARK; + (*f2) |= TR2_RES_CHAOS; + (*f2) |= TR2_HOLD_LIFE; + (*f3) |= TR3_FEATHER; + (*f3) |= TR3_REGEN; + break; + } + case MIMIC_DEMON_LORD: + { + (*f3) |= TR3_SEE_INVIS; + (*f2) |= TR2_FREE_ACT; + (*f3) |= TR3_REGEN; + (*f3) |= TR3_FEATHER; + (*f2) |= TR2_HOLD_LIFE; + (*esp) |= ESP_EVIL | ESP_GOOD | ESP_DEMON; + (*f2) |= TR2_RES_ACID; + (*f2) |= TR2_RES_ELEC; + (*f2) |= TR2_RES_FIRE; + (*f2) |= TR2_RES_POIS; + (*f2) |= TR2_RES_CONF; + (*f2) |= TR2_RES_SOUND; + (*f2) |= TR2_RES_LITE; + (*f2) |= TR2_RES_DARK; + (*f2) |= TR2_RES_CHAOS; + (*f2) |= TR2_RES_DISEN; + (*f2) |= TR2_RES_SHARDS; + (*f2) |= TR2_RES_NEXUS; + (*f2) |= TR2_RES_BLIND; + (*f2) |= TR2_RES_NETHER; + (*f2) |= TR2_RES_FEAR; + (*f2) |= TR2_REFLECT; + (*f3) |= TR3_SH_FIRE; + (*f2) |= TR2_IM_FIRE; + (*f2) |= TR2_IM_ELEC; + (*f2) |= TR2_IM_ACID; + break; + } + } + } +#endif + else + { + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + if (r_ptr->flags2 & RF2_REFLECTING) (*f2) |= TR2_REFLECT; + if (r_ptr->flags2 & RF2_REGENERATE) (*f3) |= TR3_REGEN; + if (r_ptr->flags2 & RF2_AURA_FIRE) (*f3) |= TR3_SH_FIRE; + if (r_ptr->flags2 & RF2_AURA_ELEC) (*f3) |= TR3_SH_ELEC; + if (r_ptr->flags2 & RF2_PASS_WALL) (*f3) |= TR3_WRAITH; + if (r_ptr->flags3 & RF3_SUSCEP_FIRE) (*f2) |= TR2_SENS_FIRE; + if (r_ptr->flags3 & RF3_IM_ACID) (*f2) |= TR2_RES_ACID; + if (r_ptr->flags3 & RF3_IM_ELEC) (*f2) |= TR2_RES_ELEC; + if (r_ptr->flags3 & RF3_IM_FIRE) (*f2) |= TR2_RES_FIRE; + if (r_ptr->flags3 & RF3_IM_POIS) (*f2) |= TR2_RES_POIS; + if (r_ptr->flags3 & RF3_IM_COLD) (*f2) |= TR2_RES_COLD; + if (r_ptr->flags3 & RF3_RES_NETH) (*f2) |= TR2_RES_NETHER; + if (r_ptr->flags3 & RF3_RES_NEXU) (*f2) |= TR2_RES_NEXUS; + if (r_ptr->flags3 & RF3_RES_DISE) (*f2) |= TR2_RES_DISEN; + if (r_ptr->flags3 & RF3_NO_FEAR) (*f2) |= TR2_RES_FEAR; + if (r_ptr->flags3 & RF3_NO_SLEEP) (*f2) |= TR2_FREE_ACT; + if (r_ptr->flags3 & RF3_NO_CONF) (*f2) |= TR2_RES_CONF; + if (r_ptr->flags7 & RF7_CAN_FLY) (*f3) |= TR3_FEATHER; + } + + (*f1) |= p_ptr->xtra_f1; + (*f2) |= p_ptr->xtra_f2; + (*f3) |= p_ptr->xtra_f3; + (*f4) |= p_ptr->xtra_f4; + (*f5) |= p_ptr->xtra_f5; + (*esp) |= p_ptr->xtra_esp; + + if (p_ptr->black_breath) + { + (*f4) |= TR4_BLACK_BREATH; + } + + if (p_ptr->hp_mod != 0) + { + (*f2) |= TR2_LIFE; + } +} + +/* + * Object flag names + */ +static cptr object_flag_names[192] = +{ + "Add Str", + "Add Int", + "Add Wis", + "Add Dex", + "Add Con", + "Add Chr", + "Mul Mana", + "Mul SPower", + "Add Stea.", + "Add Sear.", + "Add Infra", + "Add Tun..", + "Add Speed", + "Add Blows", + "Chaotic", + "Vampiric", + "Slay Anim.", + "Slay Evil", + "Slay Und.", + "Slay Demon", + "Slay Orc", + "Slay Troll", + "Slay Giant", + "Slay Drag.", + "Kill Drag.", + "Sharpness", + "Impact", + "Poison Brd", + "Acid Brand", + "Elec Brand", + "Fire Brand", + "Cold Brand", + + "Sust Str", + "Sust Int", + "Sust Wis", + "Sust Dex", + "Sust Con", + "Sust Chr", + "Invisible", + "Mul life", + "Imm Acid", + "Imm Elec", + "Imm Fire", + "Imm Cold", + "Sens Fire", + "Reflect", + "Free Act", + "Hold Life", + "Res Acid", + "Res Elec", + "Res Fire", + "Res Cold", + "Res Pois", + "Res Fear", + "Res Light", + "Res Dark", + "Res Blind", + "Res Conf", + "Res Sound", + "Res Shard", + "Res Neth", + "Res Nexus", + "Res Chaos", + "Res Disen", + + + + "Aura Fire", + "Aura Elec", + "Auto Curse", + NULL, + "NoTeleport", + "AntiMagic", + "WraithForm", + "EvilCurse", + NULL, + NULL, + NULL, + NULL, + "Levitate", + "Lite", + "See Invis", + NULL, + "Digestion", + "Regen", + "Xtra Might", + "Xtra Shots", + NULL, + NULL, + NULL, + NULL, + "Activate", + "Drain Exp", + "Teleport", + "Aggravate", + "Blessed", + "Cursed", + "Hvy Curse", + "Prm Curse", + + "No blows", + "Precogn.", + "B.Breath", + "Recharge", + "Fly", + "Mrg.Curse", + NULL, + NULL, + "Sentient", + "Clone", + NULL, + "Climb", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "Imm Neth", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + + "Orc.ESP", + "Troll.ESP", + "Dragon.ESP", + "Giant.ESP", + "Demon.ESP", + "Undead.ESP", + "Evil.ESP", + "Animal.ESP", + "TLord.ESP", + "Good.ESP", + "Nlive.ESP", + "Unique.ESP", + "Spider ESP", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "Full ESP", +}; + +/* + * Summarize resistances + */ +static void display_player_ben_one(int mode) +{ + int i, n, x, y, z, dispx, modetemp, xtemp; + + object_type *o_ptr; + + char dummy[80], c; + + u32b f1, f2, f3, f4, f5, esp; + + u16b b[INVEN_TOTAL - INVEN_WIELD + 1][10]; + + int d[INVEN_TOTAL - INVEN_WIELD + 1]; + + bool got; + + byte a; + + cptr name; + + /* Scan equipment */ + for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) + { + /* Index */ + n = (i - INVEN_WIELD); + + /* Object */ + o_ptr = &p_ptr->inventory[i]; + + /* Known object flags */ + object_flags_known(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Incorporate */ + b[n][0] = (u16b)(f1 & 0xFFFF); + b[n][1] = (u16b)(f1 >> 16); + b[n][2] = (u16b)(f2 & 0xFFFF); + b[n][3] = (u16b)(f2 >> 16); + b[n][4] = (u16b)(f3 & 0xFFFF); + b[n][5] = (u16b)(f3 >> 16); + b[n][6] = (u16b)(f4 & 0xFFFF); + b[n][7] = (u16b)(f4 >> 16); + b[n][8] = (u16b)(esp & 0xFFFF); + b[n][9] = (u16b)(esp >> 16); + d[n] = o_ptr->pval; + } + + /* Carried symbiote */ + n = INVEN_CARRY - INVEN_WIELD; + + /* Player flags */ + wield_monster_flags(&f1, &f2, &f3, &f4, &f5, &esp); + + /* Incorporate */ + b[n][0] = (u16b)(f1 & 0xFFFF); + b[n][1] = (u16b)(f1 >> 16); + b[n][2] = (u16b)(f2 & 0xFFFF); + b[n][3] = (u16b)(f2 >> 16); + b[n][4] = (u16b)(f3 & 0xFFFF); + b[n][5] = (u16b)(f3 >> 16); + b[n][6] = (u16b)(f4 & 0xFFFF); + b[n][7] = (u16b)(f4 >> 16); + b[n][8] = (u16b)(esp & 0xFFFF); + b[n][9] = (u16b)(esp >> 16); + + /* Index */ + n = INVEN_TOTAL - INVEN_WIELD; + + /* Player flags */ + player_flags(&f1, &f2, &f3, &f4, &f5, &esp); + + /* Incorporate */ + b[n][0] = (u16b)(f1 & 0xFFFF); + b[n][1] = (u16b)(f1 >> 16); + b[n][2] = (u16b)(f2 & 0xFFFF); + b[n][3] = (u16b)(f2 >> 16); + b[n][4] = (u16b)(f3 & 0xFFFF); + b[n][5] = (u16b)(f3 >> 16); + b[n][6] = (u16b)(f4 & 0xFFFF); + b[n][7] = (u16b)(f4 >> 16); + b[n][8] = (u16b)(esp & 0xFFFF); + b[n][9] = (u16b)(esp >> 16); + + /* Generate the equip chars */ + sprintf(dummy, " "); + for (i = 0; i < INVEN_TOTAL - INVEN_WIELD; i++) + { + /* If you have that body part then show it */ + if (p_ptr->body_parts[i]) + { + strcat(dummy, format("%c", i + 'a')); + } + } + strcat(dummy, "@"); + + /* Scan cols */ + for (x = 1; x > -1; x--) + { + /* Label */ + Term_putstr(x * 40 + 11, 3, -1, TERM_WHITE, dummy); + + /* Scan rows */ + for (y = 0; y < 16; y++) + { + if (mode == 3 && x == 1) + { + modetemp = 4; + xtemp = 0; + } + else + { + modetemp = mode; + xtemp = x; + } + + for (z = mode; z <= modetemp; z++) + { + if (mode == 3 && x == 1 && z == modetemp) xtemp = 1; + name = object_flag_names[32 * modetemp + 16 * xtemp + y]; + got = FALSE; + + /* No name */ + if (!name) continue; + + /* Dump colon */ + if (!(modetemp == 1 && x == 0 && y > 7 && y < 12)) + { + Term_putch(x * 40 + 10, y + 4, TERM_WHITE, ':'); + } + + /* Check flags */ + dispx = 0; + for (n = 0; n < INVEN_TOTAL - INVEN_WIELD + 1; n++) + { + /* Change colour every two columns */ + bool is_green = (dispx & 0x02); + a = (is_green ? TERM_GREEN : TERM_SLATE); + c = '.'; + + /* If the body part doesn't exists then skip it :) */ + if ((n < INVEN_TOTAL - INVEN_WIELD) && (!p_ptr->body_parts[n])) continue; + + /* Increment the drawing coordinates */ + dispx++; + + /* Check flag */ + if (b[n][2 * modetemp + xtemp] & (1 << y)) + { + a = (is_green ? TERM_L_GREEN : TERM_WHITE); + if (modetemp == 1 && x == 0 && y > 7 && y < 12) + { + c = '*'; + } + else if (modetemp == 0 && x == 0 && y < 14 && (y < 6 || y > 7)) + { + if (n == INVEN_TOTAL - INVEN_WIELD) + { + c = '+'; + } + else + { + c = d[n]; + if (c < 0) + { + c = -c; + a = TERM_RED; + } + c = (c > 9 ? '*' : I2D(c)); + } + } + else + { + c = '+'; + } + got = TRUE; + } + + /* HACK - Check for nether immunity and + apply to Res Neth line */ + if (modetemp == 1 && x == 1 && y == 12) + { + if (b[n][7] & (1 << 6)) + { + a = (is_green ? TERM_L_GREEN : TERM_WHITE); + c = '*'; + got = TRUE; + } + } + + /* Monochrome */ + if (!use_color) a = TERM_WHITE; + + /* Dump flag */ + if (modetemp == 1 && x == 0 && y > 7 && y < 12) + { + if (c == '*') Term_putch(40 + 11 + dispx, y - 4, a, c); + } + else + { + Term_putch(x * 40 + 11 + dispx, y + 4, a, c); + } + } + + a = TERM_WHITE; + if (use_color && got) + { + if (modetemp == 1 && x == 0 && y > 7 && y < 12) + { + a = TERM_L_GREEN; + } + else if (modetemp != 0) + { + a = TERM_GREEN; + } + } + + /* HACK - Check for nether immunity and change "Res Neth" */ + if (modetemp == 1 && x == 1 && y == 12 && p_ptr->immune_neth) + { + name = "Imm Neth"; + a = TERM_L_GREEN; + } + + /* Dump name */ + if (modetemp == 1 && x == 0 && y > 7 && y < 12) + { + if (got) Term_putstr(40, y - 4, -1, a, name); + } + else + { + Term_putstr(x * 40, y + 4, -1, a, name); + } + } + } + } +} + + +/* + * Display the character on the screen (various modes) + * + * The top two and bottom two lines are left blank. + * + * Mode 0 = standard display with skills + * Mode 1 = standard display with history + * Mode 2 = current flags (part 1) + * Mode 3 = current flags (part 2) + * Mode 4 = current flags (part 3) + * Mode 5 = current flags (part 4) + * Mode 6 = current flags (part 5 -- esp) + */ +void display_player(int mode) +{ + int i; + + char buf[80]; + + + /* Erase screen */ + clear_from(0); + + /* Standard */ + if ((mode == 0) || (mode == 1)) + { + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + /* Name, Sex, Race, Class */ + put_str("Name :", 2, 1); + put_str("Sex :", 3, 1); + put_str("Race :", 4, 1); + put_str("Class :", 5, 1); + put_str("Body :", 6, 1); + put_str("God :", 7, 1); + c_put_str(TERM_L_BLUE, player_name, 2, 9); + if (p_ptr->body_monster != 0) + { + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + char tmp[12]; + + if ((r_ptr->flags1 & RF1_MALE) != 0) + strcpy(tmp, "Male"); + else if ((r_ptr->flags1 & RF1_FEMALE) != 0) + strcpy(tmp, "Female"); + else + strcpy(tmp, "Neuter"); + c_put_str(TERM_L_BLUE, tmp, 3, 9); + } + else + c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 9); + sprintf(buf, "%s", get_player_race_name(p_ptr->prace, p_ptr->pracem)); + c_put_str(TERM_L_BLUE, buf, 4, 9); + c_put_str(TERM_L_BLUE, spp_ptr->title + c_name, 5, 9); + c_put_str(TERM_L_BLUE, r_name + r_ptr->name, 6, 9); + c_put_str(TERM_L_BLUE, deity_info[p_ptr->pgod].name, 7, 9); + + /* Age, Height, Weight, Social */ + prt_num("Age ", (int)p_ptr->age + bst(YEAR, turn - (START_DAY * 10)), 2, 32, TERM_L_BLUE, " "); + prt_num("Height ", (int)p_ptr->ht, 3, 32, TERM_L_BLUE, " "); + prt_num("Weight ", (int)p_ptr->wt, 4, 32, TERM_L_BLUE, " "); + prt_num("Social Class ", (int)p_ptr->sc, 5, 32, TERM_L_BLUE, " "); + + /* Display the stats */ + for (i = 0; i < 6; i++) + { + char punctuation = p_ptr->stat_max[i] == 18 + 100 ? '!' : ':'; + /* Special treatment of "injured" stats */ + if (p_ptr->stat_cur[i] < p_ptr->stat_max[i]) + { + int value; + int colour; + + if (p_ptr->stat_cnt[i]) + colour = TERM_ORANGE; + else + colour = TERM_YELLOW; + + /* Use lowercase stat name */ + put_str(format("%s%c ", stat_names_reduced[i], punctuation), 2 + i, 61); + + /* Get the current stat */ + value = p_ptr->stat_use[i]; + + /* Obtain the current stat (modified) */ + cnv_stat(value, buf); + + /* Display the current stat (modified) */ + c_put_str(colour, buf, 2 + i, 66); + + /* Acquire the max stat */ + value = p_ptr->stat_top[i]; + + /* Obtain the maximum stat (modified) */ + cnv_stat(value, buf); + + /* Display the maximum stat (modified) */ + c_put_str(TERM_L_GREEN, buf, 2 + i, 73); + } + + /* Normal treatment of "normal" stats */ + else + { + /* Assume uppercase stat name */ + put_str(format("%s%c ", stat_names[i], punctuation), 2 + i, 61); + + /* Obtain the current stat (modified) */ + cnv_stat(p_ptr->stat_use[i], buf); + + /* Display the current stat (modified) */ + c_put_str(TERM_L_GREEN, buf, 2 + i, 66); + } + } + + /* Extra info */ + display_player_middle(); + + /* Display "history" info */ + if (mode == 1) + { + put_str("(Character Background)", 15, 25); + + for (i = 0; i < 4; i++) + { + put_str(history[i], i + 16, 10); + } + } + + /* Display "various" info */ + else + { + put_str("(Miscellaneous Abilities)", 15, 25); + + display_player_various(); + } + } + + /* Special */ + else + { + display_player_ben_one(mode - 2); + } +} + +/* + * Utility function; should probably be in some other file... + * + * Describe the player's location -- either by dungeon level, town, or in + * wilderness with landmark reference. + */ +cptr describe_player_location() +{ + int i; + static char desc[80]; + int pwx = (p_ptr->wild_mode ? p_ptr->px : p_ptr->wilderness_x); + int pwy = (p_ptr->wild_mode ? p_ptr->py : p_ptr->wilderness_y); + int feat = wild_map[pwy][pwx].feat; + + if (dungeon_type != DUNGEON_WILDERNESS && dun_level > 0) + sprintf(desc, "on level %d of %s", dun_level, d_info[dungeon_type].name + d_name); + else if (wf_info[feat].terrain_idx == TERRAIN_TOWN) + sprintf(desc, "in the town of %s", wf_info[feat].name + wf_name); + else if (wf_info[feat].entrance) + sprintf(desc, "near %s", wf_info[feat].name + wf_name); + else + { + /* + * The complicated case. Find the nearest known landmark, + * and describe our position relative to that. Note that + * we may not even have any known landmarks (for instance, + * a Lost Soul character just after escaping the Halls of + * Mandos). + */ + int landmark = 0, lwx = 0, lwy = 0; + int l_dist = -1; + int i; + + for (i = 0; i < max_wf_idx; i++) + { + int wx = wf_info[i].wild_x; + int wy = wf_info[i].wild_y; + int dist; + + /* Skip if not a landmark */ + if (!wf_info[i].entrance) continue; + + /* Skip if we haven't seen it */ + if (!wild_map[wy][wx].known) continue; + + dist = distance(wy, wx, pwy, pwx); + if (dist < l_dist || l_dist < 0) + { + landmark = i; + l_dist = dist; + lwx = wx; + lwy = wy; + } + } + + if (!landmark) + sprintf(desc, "in %s", wf_info[feat].text + wf_text); + else if (pwx == lwx && pwy == lwy) + /* Paranoia; this should have been caught above */ + sprintf(desc, "near %s", wf_info[feat].name + wf_name); + else + { + /* + * We split the circle into eight equal octants of + * size pi/4 radians; the "east" octant, for + * instance, is defined as due east plus or minus + * pi/8 radians. Now sin(pi/8) ~= 0.3826 ~= 31/81, + * so we check |dx|/|dy| and |dy|/|dx| against that + * ratio to determine which octant we're in. + */ + int dx = pwx - lwx; + int dy = pwy - lwy; + cptr ns = (dy > 0 ? "south" : "north"); + cptr ew = (dx > 0 ? "east" : "west"); + + dx = (dx < 0 ? -dx : dx); + dy = (dy < 0 ? -dy : dy); + if (dy * 81 < dx * 31) ns = ""; + if (dx * 81 < dy * 31) ew = ""; + + sprintf(desc, "in %s %s%s of %s", + wf_info[feat].text + wf_text, ns, ew, + wf_info[landmark].name + wf_name); + } + } + + /* strip trailing whitespace */ + for (i = 0; desc[i]; ++i); + while (desc[--i] == ' ') + desc[i] = 0; + + return desc; +} + +/* + * Helper function or file_character_print_grid + * + * Figure out if a row on the grid is empty + */ +static bool file_character_print_grid_check_row(const char *buf) +{ + if (strstr(buf + 12, "+")) return TRUE; + if (strstr(buf + 12, "*")) return TRUE; + if (strstr(buf + 12, "1")) return TRUE; + if (strstr(buf + 12, "2")) return TRUE; + if (strstr(buf + 12, "3")) return TRUE; + if (strstr(buf + 12, "4")) return TRUE; + if (strstr(buf + 12, "5")) return TRUE; + if (strstr(buf + 12, "6")) return TRUE; + if (strstr(buf + 12, "7")) return TRUE; + if (strstr(buf + 12, "8")) return TRUE; + if (strstr(buf + 12, "9")) return TRUE; + return FALSE; +} + +/* + * Helper function for file_character + * + * Prints the big ugly grid + */ +static void file_character_print_grid(FILE *fff, bool show_gaps, bool show_legend) +{ + static cptr blank_line = " "; + static char buf[1024]; + byte a; + char c; + int x, y; + + y = show_legend ? 3 : 4; + for (; y < 23; y++) + { + for (x = 0; x < 40; x++) + { + (void)(Term_what(x, y, &a, &c)); + buf[x] = c; + } + + buf[x] = '\0'; + if (strcmp(buf, blank_line) && + (y == 3 || show_gaps || file_character_print_grid_check_row(buf))) + fprintf (fff, " %s\n", buf); + } + for (y = 4; y < 23; y++) + { + for (x = 40; x < 80; x++) + { + (void)(Term_what(x, y, &a, &c)); + buf[x - 40] = c; + } + + buf[x] = '\0'; + if (strcmp(buf, blank_line) && + (show_gaps || file_character_print_grid_check_row(buf))) + fprintf (fff, " %s\n", buf); + } +} + +/* + * Helper function for file_character + * + * Outputs one item (for Inventory, Equipment, Home, and Mathom-house) + */ +void file_character_print_item(FILE *fff, char label, object_type *obj, bool full) +{ + static char o_name[80]; + static cptr paren = ")"; + object_desc(o_name, obj, TRUE, 3); + fprintf(fff, "%c%s %s\n", label, paren, o_name); + + if ((artifact_p(obj) || ego_item_p(obj) || obj->tval == TV_RING || obj->tval == TV_AMULET || full) && + (obj->ident & IDENT_MENTAL)) + { + object_out_desc(obj, fff, TRUE, TRUE); + } +} + +/* + * Helper function for file_character + * + * Prints out one "store" (for Home and Mathom-house) + */ +void file_character_print_store(FILE *fff, wilderness_type_info *place, int store, bool full) +{ + int i; + town_type *town = &town_info[place->entrance]; + store_type *st_ptr = &town->store[store]; + + if (st_ptr->stock_num) + { + /* Header with name of the town */ + fprintf(fff, " [%s Inventory - %s]\n\n", st_name + st_info[store].name, wf_name + place->name); + + /* Dump all available items */ + for (i = 0; i < st_ptr->stock_num; i++) + { + file_character_print_item(fff, I2A(i%24), &st_ptr->stock[i], full); + } + + /* Add an empty line */ + fprintf(fff, "\n\n"); + } +} + +/* + * Helper function for file_character + * + * Checks if the store hasn't been added to the list yet, and then adds it if it + * was not already there. XXX This is an ugly workaround for the double Gondolin + * problem. + * + * Beware of the ugly pointer gymnastics. + */ +bool file_character_check_stores(store_type ***store_list, int *store_list_count, wilderness_type_info *place, int store) +{ + town_type *town = &town_info[place->entrance]; + store_type *st_ptr = &town->store[store]; + store_type **head = *store_list; + int i; + + /* check the list for this store */ + for (i = 0; i < *store_list_count; ++i) + { + if (*head == st_ptr) return FALSE; + ++head; + } + + /* make room for the new one in the array */ + if (*store_list) + { + head = *store_list; + *store_list = C_RNEW(*store_list_count + 1, store_type *); + C_COPY(*store_list, head, *store_list_count, store_type *); + C_FREE(head, *store_list_count, store_type *); + } + else + { + *store_list = RNEW(store_type *); + } + + /* update data */ + (*store_list)[*store_list_count] = st_ptr; + ++*store_list_count; + + return TRUE; +} + +/* + * 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, j, x, y; + byte a; + char c; + int fd = -1; + FILE *fff = NULL; + char buf[1024]; + store_type **store_list = NULL; + int store_list_count = 0; + + /* 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 */ + (void)fd_close(fd); + + /* Build query */ + (void)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"); + + /* Invalid file */ + if (!fff) + { + /* Message */ + msg_format("Character sheet creation failed!"); + msg_print(NULL); + + /* Error */ + return ( -1); + } + + + /* Begin dump */ + fprintf(fff, " [%s %ld.%ld.%ld%s Character Sheet]\n\n", + game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, IS_CVS); + + + /* Display player */ + display_player(0); + + /* Dump part of the screen */ + for (y = 2; y < 22; 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; + } + + /* Terminate */ + buf[x] = '\0'; + + /* End the row */ + fprintf(fff, "%s\n", buf); + } + + /* Display history */ + display_player(1); + + /* Dump part of the screen */ + for (y = 15; y < 20; 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; + } + + /* Terminate */ + buf[x] = '\0'; + + /* End the row */ + fprintf(fff, "%s\n", buf); + } + + /* List the patches */ + hook_file = fff; + exec_lua("patchs_list()"); + + fprintf(fff, "\n\n [Miscellaneous information]\n"); + if (joke_monsters) + fprintf(fff, "\n Joke monsters: ON"); + else + fprintf(fff, "\n Joke monsters: OFF"); + + if (p_ptr->maximize) + fprintf(fff, "\n Maximize mode: ON"); + else + fprintf(fff, "\n Maximize mode: OFF"); + + if (p_ptr->preserve) + fprintf(fff, "\n Preserve Mode: ON"); + else + fprintf(fff, "\n Preserve Mode: OFF"); + + if (auto_scum) + fprintf(fff, "\n Autoscum: ON"); + else + fprintf(fff, "\n Autoscum: OFF"); + + if (always_small_level) + fprintf(fff, "\n Small Levels: ALWAYS"); + else if (small_levels) + fprintf(fff, "\n Small Levels: ON"); + else + fprintf(fff, "\n Small Levels: OFF"); + + if (empty_levels) + fprintf(fff, "\n Arena Levels: ON"); + else + fprintf(fff, "\n Arena Levels: OFF"); + + if (ironman_rooms) + fprintf(fff, "\n Always unusual rooms: ON"); + else + fprintf(fff, "\n Always unusual rooms: OFF"); + + if (seed_dungeon) + fprintf(fff, "\n Persistent Dungeons: ON"); + else + fprintf(fff, "\n Persistent Dungeons: OFF"); + + fprintf(fff, "\n\n Recall Depth:"); + for (y = 1; y < max_d_idx; y++) + { + if (max_dlv[y]) + fprintf(fff, "\n %s: Level %d (%d')", + d_name + d_info[y].name, + max_dlv[y], 50 * (max_dlv[y])); + } + fprintf(fff, "\n"); + + if (noscore) + fprintf(fff, "\n You have done something illegal."); + + if (PRACE_FLAGS(PR1_EXPERIMENTAL) || seed_dungeon) + fprintf(fff, "\n You have done something experimental."); + + if (stupid_monsters) + fprintf(fff, "\n Your opponents are behaving stupidly."); + + { + char desc[80]; + cptr mimic; + + monster_race_desc(desc, p_ptr->body_monster, 0); + fprintf(fff, "\n Your body %s %s.", (death ? "was" : "is"), desc); + + if (p_ptr->tim_mimic) + { + call_lua("get_mimic_info", "(d,s)", "s", p_ptr->mimic_form, "name", &mimic); + fprintf(fff, "\n You %s disguised as a %s.", (death ? "were" : "are"), mimic); + } + } + + /* Where we are, if we're alive */ + if (!death) fprintf(fff, "\n You are currently %s.", describe_player_location()); + + /* Monsters slain */ + { + int k; + s32b Total = 0; + + for (k = 1; k < max_r_idx; k++) + { + monster_race *r_ptr = &r_info[k]; + + if (r_ptr->flags1 & (RF1_UNIQUE)) + { + bool dead = (r_ptr->max_num == 0); + if (dead) + { + Total++; + } + } + else + { + s16b This = r_ptr->r_pkills; + if (This > 0) + { + Total += This; + } + } + } + + if (Total < 1) + fprintf(fff, "\n You have defeated no enemies yet."); + else if (Total == 1) + fprintf(fff, "\n You have defeated one enemy."); + else + fprintf(fff, "\n You have defeated %lu enemies.", Total); + } + + hook_file = fff; + process_hooks(HOOK_CHAR_DUMP, "()"); + + /* Date */ + { + char buf2[20]; + u32b days = bst(DAY, turn - (START_DAY * 10)); + + strnfmt(buf2, 20, get_day(bst(YEAR, START_DAY * 10) + START_YEAR)); + fprintf(fff, "\n\n You started your adventure the %s of the %s year of the third age.", + get_month_name(bst(DAY, START_DAY * 10), wizard, FALSE), buf2); + + strnfmt(buf2, 20, get_day(bst(YEAR, turn) + START_YEAR)); + fprintf(fff, "\n %s the %s of the %s year of the third age.", + (death ? "You ended your adventure" : "It is currently"), + get_month_name(bst(DAY, turn), wizard, FALSE), buf2); + fprintf(fff, + (death ? "\n Your adventure lasted %ld day%s." : "\n You have been adventuring for %ld day%s."), + days, (days == 1) ? "" : "s"); + } + + fprintf (fff, "\n\n"); + + /* Emit the self-knowledge lines, even though they duplicate the + information in the grids (below), because they contain information + that's not in the grids (racial abilities, luck, etc.). */ + if (full) + { + self_knowledge(fff); + fprintf(fff, "\n\n"); + } + + /* adds and slays */ + display_player (2); + file_character_print_grid(fff, FALSE, TRUE); + + /* sustains and resistances */ + display_player (3); + file_character_print_grid(fff, TRUE, FALSE); + + /* stuff */ + display_player (4); + file_character_print_grid(fff, FALSE, FALSE); + + /* a little bit of stuff */ + display_player (5); + file_character_print_grid(fff, FALSE, FALSE); + + /* Dump corruptions */ + if (got_corruptions()) + { + fprintf(fff, "\n Corruption list:\n"); + dump_corruptions(fff, FALSE); + } + + /* Dump skills */ + dump_skills(fff); + dump_abilities(fff); + + if (p_ptr->companion_killed) + { + if (p_ptr->companion_killed == 1) + fprintf(fff, "\n One of your companion(s) has been killed."); + else + fprintf(fff, "\n %d of your companions have been killed.", p_ptr->companion_killed); + } + + for (i = 0; i < MAX_FATES; i++) + { + if ((fates[i].fate) && (fates[i].know)) + { + fprintf(fff, "\n\n [Fates]\n\n"); + dump_fates(fff); + break; + } + } + + /* Skip some lines */ + fprintf(fff, "\n\n"); + + + /* Dump the equipment */ + text_out_indent = 4; + if (equip_cnt) + { + fprintf(fff, " [Character Equipment]\n\n"); + for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) + { + if (!p_ptr->body_parts[i - INVEN_WIELD]) continue; + + file_character_print_item(fff, index_to_label(i), &p_ptr->inventory[i], full); + } + fprintf(fff, "\n\n"); + } + + /* Dump the inventory */ + fprintf(fff, " [Character Inventory]\n\n"); + for (i = 0; i < INVEN_PACK; i++) + { + file_character_print_item(fff, index_to_label(i), &p_ptr->inventory[i], full); + } + fprintf(fff, "\n\n"); + + /* Print all homes in the different towns */ + for (j = 0; j < max_wf_idx; j++) + { + if (wf_info[j].feat == FEAT_TOWN && + file_character_check_stores(&store_list, &store_list_count, &wf_info[j], 7)) + file_character_print_store(fff, &wf_info[j], 7, full); + } + store_list = C_FREE(store_list, store_list_count, store_type *); + store_list_count = 0; + + /* Print all Mathom-houses in the different towns */ + for (j = 0; j < max_wf_idx; j++) + { + if (wf_info[j].feat == FEAT_TOWN && + file_character_check_stores(&store_list, &store_list_count, &wf_info[j], 57)) + file_character_print_store(fff, &wf_info[j], 57, full); + } + store_list = C_FREE(store_list, store_list_count, store_type *); + store_list_count = 0; + + text_out_indent = 0; + + /* Close it */ + my_fclose(fff); + + + /* Message */ + msg_print("Character sheet creation 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. + * + * XXX XXX XXX Consider using a temporary file. + * + * XXX XXX XXX Allow the user to "save" the current file. + */ + +/* + * A structure to hold (some of == XXX) the hyperlink information. + * This prevents excessive use of stack. + */ +#define MAX_LINKS 1024 +struct hyperlink +{ + /* Path buffer */ + char path[1024]; + + /* General buffer */ + char rbuf[1024]; + + /* Hold a string to find */ + char finder[81]; + + /* Hold a string to show */ + char shower[81]; + + /* Describe this thing */ + char caption[128]; + + /* Hypertext info */ + char link[MAX_LINKS][32], link_key[MAX_LINKS]; + int link_x[MAX_LINKS], link_y[MAX_LINKS], link_line[MAX_LINKS]; +}; + +typedef struct hyperlink hyperlink_type; + +bool show_file(cptr name, cptr what, int line, int mode) +{ + int i, k, x; + + byte link_color = TERM_ORANGE, link_color_sel = TERM_YELLOW; + + /* 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; + + /* Color of the next line */ + byte color = TERM_WHITE; + + /* This screen has sub-screens */ + bool menu = FALSE; + + /* Current help file */ + FILE *fff = NULL; + + /* Find this string (if any) */ + cptr find = NULL; + + /* Char array type of hyperlink info */ + hyperlink_type *h_ptr; + + /* Pointer to general buffer in the above */ + char *buf; + + int cur_link = 0, max_link = 0; + + /* Read size of screen for big-screen stuff -pav- */ + int wid, hgt; + + /* Allocate hyperlink data */ + MAKE(h_ptr, hyperlink_type); + + /* Setup buffer pointer */ + buf = h_ptr->rbuf; + + /* Wipe the links */ + for (i = 0; i < MAX_LINKS; i++) + { + h_ptr->link_x[i] = -1; + } + + /* Hack XXX XXX XXX */ + if (what) + { + /* h_ptr->caption */ + strcpy(h_ptr->caption, what); + + /* Access the "file" */ + strcpy(h_ptr->path, name); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open */ + fff = my_fopen(h_ptr->path, "r"); + + /* Drop permission */ + safe_setuid_drop(); + } + + /* Look in "help" */ + if (!fff) + { + /* h_ptr->caption */ + sprintf(h_ptr->caption, "Help file '%s'", name); + + /* Build the filename */ + path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, name); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the file */ + fff = my_fopen(h_ptr->path, "r"); + + /* Drop permission */ + safe_setuid_drop(); + } + + /* Look in "info" */ + if (!fff) + { + /* h_ptr->caption */ + sprintf(h_ptr->caption, "Info file '%s'", name); + + /* Build the filename */ + path_build(h_ptr->path, 1024, ANGBAND_DIR_INFO, name); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the file */ + fff = my_fopen(h_ptr->path, "r"); + + /* Drop permission */ + safe_setuid_drop(); + } + + /* Look in "file" */ + if (!fff) + { + /* h_ptr->caption */ + sprintf(h_ptr->caption, "File '%s'", name); + + /* Build the filename */ + path_build(h_ptr->path, 1024, ANGBAND_DIR_FILE, name); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the file */ + fff = my_fopen(h_ptr->path, "r"); + + /* Drop permission */ + safe_setuid_drop(); + } + + /* Oops */ + if (!fff) + { + /* Message */ + msg_format("Cannot open '%s'.", name); + msg_print(NULL); + + /* Free hyperlink info */ + KILL(h_ptr, hyperlink_type); + + /* Oops */ + return (TRUE); + } + + + /* Pre-Parse the file */ + while (TRUE) + { + /* Read a line or stop */ + if (my_fgets(fff, h_ptr->rbuf, 1024)) break; + + /* Get a color */ + if (prefix(h_ptr->rbuf, "#####")) + { + buf = &h_ptr->rbuf[6]; + } + else buf = h_ptr->rbuf; + + /* Get the link colors */ + if (prefix(buf, "|||||")) + { + link_color = color_char_to_attr(buf[5]); + link_color_sel = color_char_to_attr(buf[6]); + } + + /* Tag ? */ + if (prefix(buf, "~~~~~")) + { + if (line < 0) + { + int i; + char old_c; + + for (i = 5; (buf[i] >= '0') && (buf[i] <= '9'); i++) + ; + old_c = buf[i]; + buf[i] = '\0'; + + if (atoi(buf + 5) == -line) + { + line = next + 1; + } + buf[i] = old_c; + } + } + + x = 0; + while (buf[x]) + { + /* Hyperlink ? */ + if (prefix(buf + x, "*****")) + { + int xx = x + 5, stmp, xdeb = x + 5, z; + char tmp[20]; + + for (z = 0; z < 20; z++) tmp[z] = '\0'; + + h_ptr->link_x[max_link] = x; + h_ptr->link_y[max_link] = next; + + if (buf[xx] == '/') + { + xx++; + h_ptr->link_key[max_link] = buf[xx]; + xx++; + xdeb += 2; + } + else + { + h_ptr->link_key[max_link] = 0; + } + + /* Zap the link info */ + while (buf[xx] != '*') + { + h_ptr->link[max_link][xx - xdeb] = buf[xx]; + xx++; + } + h_ptr->link[max_link][xx - xdeb] = '\0'; + xx++; + stmp = xx; + while (buf[xx] != '[') + { + tmp[xx - stmp] = buf[xx]; + xx++; + } + xx++; + tmp[xx - stmp] = '\0'; + h_ptr->link_line[max_link] = -atoi(tmp); + max_link++; + } + x++; + } + + /* Count the "real" lines */ + next++; + } + + /* Save the number of "real" lines */ + size = next; + + + + /* Display the file */ + while (TRUE) + { + /* Clear screen */ + Term_clear(); + + Term_get_size(&wid, &hgt); + + /* Restart when necessary */ + if (line >= size) line = 0; + + + /* Re-open the file if needed */ + if (next > line) + { + /* Close it */ + my_fclose(fff); + + /* Grab permission */ + safe_setuid_grab(); + + /* Hack -- Re-Open the file */ + fff = my_fopen(h_ptr->path, "r"); + + /* Drop permission */ + safe_setuid_drop(); + + + /* Oops */ + if (!fff) + { + /* Free hyperlink info */ + KILL(h_ptr, hyperlink_type); + + return (FALSE); + } + + /* 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 (or more in bigscreen) lines of the file */ + for (i = 0; i < (hgt - 4); ) + { + int print_x; + + /* Hack -- track the "first" line */ + if (!i) line = next; + + /* Get a line of the file or stop */ + if (my_fgets(fff, h_ptr->rbuf, 1024)) break; + + /* Get a color */ + if (prefix(h_ptr->rbuf, "#####")) + { + color = color_char_to_attr(h_ptr->rbuf[5]); + buf = &h_ptr->rbuf[6]; + } + else buf = h_ptr->rbuf; + + /* Count the "real" lines */ + next++; + + /* Skip link colors */ + if (prefix(buf, "|||||")) continue; + + /* Skip tags */ + if (prefix(buf, "~~~~~")) + { + i++; + continue; + } + + /* Hack -- keep searching */ + if (find && !i && !strstr(buf, find)) continue; + + /* Hack -- stop searching */ + find = NULL; + + /* Be sure to get a correct cur_link */ + if (h_ptr->link_y[cur_link] >= line + (hgt - 4)) + { + while ((cur_link > 0) && (h_ptr->link_y[cur_link] >= line + (hgt - 4))) + { + cur_link--; + } + } + if (h_ptr->link_y[cur_link] < line) + { + while ((cur_link < max_link) && (h_ptr->link_y[cur_link] < line)) + { + cur_link++; + } + } + + /* Dump the line */ + print_x = 0; + if (!prefix(buf, "&&&&&")) + { + x = 0; + while (buf[x]) + { + /* Hyperlink ? */ + if (prefix(buf + x, "*****")) + { + int xx = x + 5; + + /* Zap the link info */ + while (buf[xx] != '[') + { + xx++; + } + xx++; + /* Ok print the link name */ + while (buf[xx] != ']') + { + byte color = link_color; + + if ((h_ptr->link_x[cur_link] == x) && (h_ptr->link_y[cur_link] == line + i)) + color = link_color_sel; + + /* Now we treat the next char as printable */ + if (buf[xx] == '\\') + xx++; + + Term_putch(print_x, i + 2, color, buf[xx]); + xx++; + print_x++; + } + x = xx; + } + /* Color ? */ + else if (prefix(buf + x, "[[[[[")) + { + int xx = x + 6; + + /* Ok print the link name */ + while (buf[xx] != ']') + { + /* Now we treat the next char as printable */ + if (buf[xx] == '\\') + xx++; + Term_putch(print_x, i + 2, color_char_to_attr(buf[x + 5]), buf[xx]); + xx++; + print_x++; + } + x = xx; + } + /* Remove HTML ? */ + else if (prefix(buf + x, "{{{{{")) + { + int xx = x + 6; + + /* Ok remove this section */ + while (buf[xx] != '}') + { + xx++; + } + x = xx; + } + else + { + Term_putch(print_x, i + 2, color, buf[x]); + print_x++; + } + + x++; + } + } + /* Verbatim mode: i.e: acacacac */ + else + { + x = 5; + while (buf[x]) + { + Term_putch(print_x, i + 2, color_char_to_attr(buf[x]), buf[x + 1]); + print_x++; + x += 2; + } + } + color = TERM_WHITE; + + /* Hilite "h_ptr->shower" */ + if (h_ptr->shower[0]) + { + cptr str = buf; + + /* Display matches */ + while ((str = strstr(str, h_ptr->shower)) != NULL) + { + int len = strlen(h_ptr->shower); + + /* Display the match */ + Term_putstr(str - buf, i + 2, len, TERM_YELLOW, h_ptr->shower); + + /* Advance */ + str += len; + } + } + + /* Count the printed lines */ + i++; + } + + /* Hack -- failed search */ + if (find) + { + bell(); + line = back; + find = NULL; + continue; + } + + + /* Show a general "title" */ + prt(format("[%s %ld.%ld.%ld, %s, Line %d/%d]", game_module, + VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, + h_ptr->caption, line, size), 0, 0); + + /* Prompt -- menu screen */ + if (menu) + { + /* Wait for it */ + prt("[Press a Number, or ESC to exit.]", hgt - 1, 0); + } + + /* Prompt -- small files */ + else if (size <= (hgt - 4)) + { + /* Wait for it */ + prt("[Press ESC to exit.]", hgt - 1, 0); + } + + /* Prompt -- large files */ + else + { + /* Wait for it */ + prt("[Press 2, 8, 4, 6, /, =, #, %, backspace, or ESC to exit.]", hgt - 1, 0); + } + + /* Get a keypress */ + k = inkey(); + + /* Hack -- return to last screen */ + if ((k == '?') || (k == 0x7F) || (k == '\010')) break; + + /* Hack -- try showing */ + if (k == '=') + { + /* Get "h_ptr->shower" */ + prt("Show: ", hgt - 1, 0); + (void)askfor_aux(h_ptr->shower, 80); + } + + /* Hack -- try finding */ + if (k == '/') + { + /* Get "h_ptr->finder" */ + prt("Find: ", hgt - 1, 0); + if (askfor_aux(h_ptr->finder, 80)) + { + /* Find it */ + find = h_ptr->finder; + back = line; + line = line + 1; + + /* Show it */ + strcpy(h_ptr->shower, h_ptr->finder); + } + } + + /* Hack -- go to a specific line */ + if (k == '#') + { + char tmp[81]; + prt("Goto Line: ", hgt - 1, 0); + strcpy(tmp, "0"); + if (askfor_aux(tmp, 80)) + { + line = atoi(tmp); + } + } + + /* Hack -- go to a specific file */ + if (k == '%') + { + char tmp[81]; + prt("Goto File: ", hgt - 1, 0); + strcpy(tmp, "help.hlp"); + if (askfor_aux(tmp, 80)) + { + if (!show_file(tmp, NULL, 0, mode)) k = ESCAPE; + } + } + + /* Hack -- Allow backing up */ + if (k == '-') + { + line = line - (hgt - 4); + if (line < 0) line = 0; + } + + if (k == '8') + { + line--; + if (line < 0) line = 0; + } + + /* Hack -- Advance a single line */ + if (k == '2') + { + line = line + 1; + } + + /* Advance one page */ + if (k == ' ') + { + line = line + (hgt - 4); + } + + /* Advance one link */ + if ((k == '6') || (k == '\t')) + { + cur_link++; + if (cur_link >= max_link) cur_link = max_link - 1; + + if (h_ptr->link_y[cur_link] < line) line = h_ptr->link_y[cur_link]; + if (h_ptr->link_y[cur_link] >= line + (hgt - 4)) line = h_ptr->link_y[cur_link] - (hgt - 4); + } + /* Return one link */ + if (k == '4') + { + cur_link--; + if (cur_link < 0) cur_link = 0; + + if (h_ptr->link_y[cur_link] < line) line = h_ptr->link_y[cur_link]; + if (h_ptr->link_y[cur_link] >= line + (hgt - 4)) line = h_ptr->link_y[cur_link] - (hgt - 4); + } + + /* Recurse on numbers */ + if (k == '\r') + { + if (h_ptr->link_x[cur_link] != -1) + { + /* Recurse on that file */ + if (!show_file(h_ptr->link[cur_link], NULL, h_ptr->link_line[cur_link], mode)) k = ESCAPE; + } + } + + /* Exit on escape */ + if (k == ESCAPE) break; + + /* No other key ? lets look for a shortcut */ + for (i = 0; i < max_link; i++) + { + if (h_ptr->link_key[i] == k) + { + /* Recurse on that file */ + if (!show_file(h_ptr->link[i], NULL, h_ptr->link_line[i], mode)) k = ESCAPE; + break; + } + } + } + + /* Close the file */ + my_fclose(fff); + + /* Free hyperlink buffers */ + KILL(h_ptr, hyperlink_type); + + /* Escape */ + if (k == ESCAPE) return (FALSE); + + /* Normal return */ + return (TRUE); +} + +bool txt_to_html(cptr head, cptr foot, cptr base, cptr ext, bool force, bool recur) +{ + int i, x; + + /* Number of "real" lines passed by */ + int next = 0; + + /* Number of "real" lines in the file */ + int size = 0; + + char buf_name[80]; + + /* Color of the next line */ + byte color = TERM_WHITE; + + /* Current help file */ + FILE *fff = NULL; + + /* Current aux file */ + FILE *aux = NULL; + + /* Current html file */ + FILE *htm = NULL; + + /* Char array type of hyperlink info */ + hyperlink_type *h_ptr; + + cptr file_ext; + cptr link_prefix; + cptr link_suffix; + + /* Pointer to general buffer in the above */ + char *buf; + + /* Allocate hyperlink data */ + MAKE(h_ptr, hyperlink_type); + + /* Setup buffer pointer */ + buf = h_ptr->rbuf; + + /* Wipe the links */ + for (i = 0; i < MAX_LINKS; i++) + { + h_ptr->link_x[i] = -1; + } + + /* Parse it(yeah lua is neat :) */ + tome_dofile_anywhere(ANGBAND_DIR_HELP, "def.aux", TRUE); + + /* Ok now get the parameters */ + file_ext = string_exec_lua("return file_ext"); + link_prefix = string_exec_lua("return link_prefix"); + link_suffix = string_exec_lua("return link_suffix"); + + sprintf(buf_name, "%s.%s", base, file_ext); + + if ((!force) && file_exist(buf_name)) return FALSE; + + /* Build the filename */ + path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, buf_name); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the file */ + htm = my_fopen(h_ptr->path, "w"); + + /* Drop permission */ + safe_setuid_drop(); + + sprintf(buf_name, "%s.%s", base, ext); + + /* h_ptr->caption */ + sprintf(h_ptr->caption, "Help file '%s'", buf_name); + + /* Build the filename */ + path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, buf_name); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the file */ + fff = my_fopen(h_ptr->path, "r"); + + /* Drop permission */ + safe_setuid_drop(); + + /* Oops */ + if (!fff || !htm) + { + /* Free hyperlink info */ + KILL(h_ptr, hyperlink_type); + + my_fclose(fff); + my_fclose(htm); + + /* Oops */ + return (TRUE); + } + + /* Save the number of "real" lines */ + size = next; + + /* Build the filename */ + path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, head); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the file */ + aux = my_fopen(h_ptr->path, "r"); + + /* Drop permission */ + safe_setuid_drop(); + + /* Copy the header */ + if (aux) + { + while (TRUE) + { + char *find; + + if (my_fgets(aux, h_ptr->rbuf, 1024)) break; + find = strstr(h_ptr->rbuf, "%t"); + if (find != NULL) + { + *find = '\0'; + find += 2; + fprintf(htm, "%s", h_ptr->rbuf); + fprintf(htm, "%s", base); + fprintf(htm, "%s\n", find); + } + else + fprintf(htm, "%s\n", h_ptr->rbuf); + } + my_fclose(aux); + } + + /* Display the file */ + while (TRUE) + { + bool do_color = FALSE; + + /* Skip a line */ + if (my_fgets(fff, h_ptr->rbuf, 1024)) break; + + color = TERM_WHITE; + + { + int print_x; + + /* Get a color */ + if (prefix(h_ptr->rbuf, "#####")) + { + color = color_char_to_attr(h_ptr->rbuf[5]); + do_color = TRUE; + fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">", + angband_color_table[color][1], + angband_color_table[color][2], + angband_color_table[color][3]); + buf = &h_ptr->rbuf[6]; + } + else buf = h_ptr->rbuf; + + /* Count the "real" lines */ + next++; + + /* Skip link colors */ + if (prefix(buf, "|||||")) continue; + + /* Skip tags */ + if (prefix(buf, "~~~~~")) + { + int i; + + for (i = 5; (buf[i] >= '0') && (buf[i] <= '9'); i++) + ; + buf[i] = '\0'; + fprintf(htm, "<A NAME=\"%s\"></A>", buf + 5); + continue; + } + + /* Dump the line */ + print_x = 0; + if (!prefix(buf, "&&&&&")) + { + x = 0; + while (buf[x]) + { + /* Hyperlink ? */ + if (prefix(buf + x, "*****")) + { + int xx = x + 5, z = 0; + char buff[80]; + char link_line[80], *s; + + if (buf[xx] == '/') xx += 2; + + /* Zap the link info */ + while (buf[xx] != '*') + { + buff[z++] = buf[xx]; + xx++; + } + xx++; + buff[z] = '\0'; + + /* Zap the link info */ + z = 0; + while (buf[xx] != '[') + { + link_line[z++] = buf[xx]; + xx++; + } + xx++; + link_line[z] = '\0'; + + /* parse it */ + s = buff; + while (*s != '.') s++; + *s = '\0'; + s++; + if (recur) txt_to_html(head, foot, buff, s, FALSE, recur); + + if (atoi(link_line)) fprintf(htm, "<A HREF=\"%s%s.%s%s#%d\">", link_prefix, buff, file_ext, link_suffix, atoi(link_line)); + else fprintf(htm, "<A HREF=\"%s%s.%s%s\">", link_prefix, buff, file_ext, link_suffix); + + /* Ok print the link name */ + while (buf[xx] != ']') + { + /* Now we treat the next char as printable */ + if (buf[xx] == '\\') + xx++; + fprintf(htm, "%c", buf[xx]); + xx++; + print_x++; + } + x = xx; + + fprintf(htm, "</A>"); + } + /* Color ? */ + else if (prefix(buf + x, "[[[[[")) + { + int xx = x + 6; + + color = color_char_to_attr(buf[x + 5]); + fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">", + angband_color_table[color][1], + angband_color_table[color][2], + angband_color_table[color][3]); + + /* Ok print the link name */ + while (buf[xx] != ']') + { + /* Now we treat the next char as printable */ + if (buf[xx] == '\\') + xx++; + fprintf(htm, "%c", buf[xx]); + xx++; + print_x++; + } + x++; + x = xx; + + fprintf(htm, "</FONT>"); + } + /* Hidden HTML tag? */ + else if (prefix(buf + x, "{{{{{")) + { + int xx = x + 5; + + /* Ok output the tag inside */ + while (buf[xx] != '}') + { + fprintf(htm, "%c", buf[xx]); + xx++; + } + x++; + x = xx; + } + else + { + fprintf(htm, "%c", buf[x]); + print_x++; + } + + x++; + } + } + /* Verbatim mode: i.e: acacacac */ + else + { + byte old_color; + + x = 5; + old_color = color_char_to_attr(buf[x]); + fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">", + angband_color_table[color][1], + angband_color_table[color][2], + angband_color_table[color][3]); + while (buf[x]) + { + color = color_char_to_attr(buf[x]); + if (color != old_color) + fprintf(htm, "</FONT><FONT COLOR=\"#%02X%02X%02X\">", + angband_color_table[color][1], + angband_color_table[color][2], + angband_color_table[color][3]); + + fprintf(htm, "%c", buf[x + 1]); + print_x++; + x += 2; + } + fprintf(htm, "</FONT>"); + } + } + if (do_color) + { + fprintf(htm, "</FONT>"); + } + fprintf(htm, "\n"); + } + + /* Build the filename */ + path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, foot); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the file */ + aux = my_fopen(h_ptr->path, "r"); + + /* Drop permission */ + safe_setuid_drop(); + + /* Copy the footer */ + if (aux) + { + while (TRUE) + { + if (my_fgets(aux, h_ptr->rbuf, 1024)) break; + fprintf(htm, "%s\n", h_ptr->rbuf); + } + my_fclose(aux); + } + + /* Close the file */ + my_fclose(htm); + my_fclose(fff); + + /* Free hyperlink buffers */ + KILL(h_ptr, hyperlink_type); + + /* Normal return */ + return (TRUE); +} + +/* Take an help file screenshot(yes yes I know..) */ +void help_file_screenshot(cptr name) +{ + int y, x; + int wid, hgt; + + byte a = 0; + char c = ' '; + + FILE *htm; + + char buf[1024]; + + /* The terms package supports up to 255x255 screen size */ + char abuf[256]; + char cbuf[256]; + + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_USER, name); + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Append to the file */ + htm = my_fopen(buf, "w"); + + /* Oops */ + if (!htm) return; + + /* Retrieve current screen size */ + Term_get_size(&wid, &hgt); + + /* Dump the screen */ + for (y = 0; y < hgt; y++) + { + cmovie_clean_line(y, abuf, cbuf); + + /* Dump each row */ + fprintf(htm, "&&&&&"); + for (x = 0; x < wid; x++) + { + a = abuf[x]; + c = cbuf[x]; + + fprintf(htm, "%c%c", a, c); + } + + /* End the row */ + fprintf(htm, "\n"); + } + + /* Close it */ + my_fclose(htm); +} + +/* Take an html screenshot */ +void html_screenshot(cptr name) +{ + int y, x; + int wid, hgt; + + byte a = 0, oa = TERM_WHITE; + char c = ' '; + + FILE *htm; + + char buf[1024]; + + /* The terms package supports up to 255x255 screen size */ + char abuf[256]; + char cbuf[256]; + + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_USER, name); + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Append to the file */ + htm = my_fopen(buf, "w"); + + /* Oops */ + if (!htm) return; + + /* Retrieve current screen size */ + Term_get_size(&wid, &hgt); + + fprintf(htm, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" + "<head>\n"); + fprintf(htm, "<meta name=\"GENERATOR\" content=\"%s %ld.%ld.%ld\"/>\n", + game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); + fprintf(htm, "<title>%s</title>\n", name); + fprintf(htm, "</head>\n" + "<body>\n" + "<pre style=\"color: #ffffff; background-color: #000000; font-family: monospace\">\n"); + fprintf(htm, "<span style=\"color: #%02X%02X%02X\">\n", + angband_color_table[TERM_WHITE][1], + angband_color_table[TERM_WHITE][2], + angband_color_table[TERM_WHITE][3]); + + /* Dump the screen */ + for (y = 0; y < hgt; y++) + { + cmovie_clean_line(y, abuf, cbuf); + + /* Dump each row */ + for (x = 0; x < wid; x++) + { + a = color_char_to_attr(abuf[x]); + c = cbuf[x]; + + if (oa != a) + { + fprintf(htm, "</span><span style=\"color: #%02X%02X%02X\">", angband_color_table[a][1], angband_color_table[a][2], angband_color_table[a][3]); + oa = a; + } + if (c == '<') + fprintf(htm, "<"); + else if (c == '>') + fprintf(htm, ">"); + else if (c == '&') + fprintf(htm, "&"); + else + fprintf(htm, "%c", c); + } + + /* End the row */ + fprintf(htm, "\n"); + } + fprintf(htm, "</span>\n" + "</pre>\n" + "</body>\n" + "</html>\n"); + + /* Close it */ + my_fclose(htm); +} + +/* + * Because this is dead code and hardly anyone but DG needs it. + * IMHO this should never been included in the game code -- pelpel + */ +#if !defined(WINDOWS) && !defined(MACINTOSH) && !defined(ACORN) + +#define KEY_NUM 9 +static int keys_tab[KEY_NUM] = +{ + 'I', 'G', 'M', 'O', 'P', 'm', 'D', 'B', 'V', +}; + +static cptr keys_desc[KEY_NUM] = +{ + "Interface changes:", + "Gameplay changes:", + "Monster changes:", + "Object changes:", + "Player changes:", + "Misc changes:", + "Dungeon changes:", + "Bug fixes:", + "Version:", +}; + +static int get_key(char c) +{ + int i; + + i = 0; + while (keys_tab[i] != c) + i++; + return ((i > KEY_NUM) ? KEY_NUM : i); +} + +/* + * Some ports don't like huge stacks + */ +typedef char chg_type[500][100]; + +bool chg_to_txt(cptr base, cptr newname) +{ + int i, j, key = 0; + + char buf[1024]; + + int lens[KEY_NUM] = {0, 0, 0, 0, 0, 0, 0, 0}; + chg_type *strs; + + /* Current chg file */ + FILE *fff = NULL; + + /* Current txt file */ + FILE *txt = NULL; + + /* Open the file */ + fff = my_fopen(base, "r"); + + /* Oops */ + if (!fff) + { + my_fclose(fff); + my_fclose(txt); + + /* Oops */ + return (TRUE); + } + + /* Count the file */ + while (TRUE) + { + /* Skip a line */ + if (my_fgets(fff, buf, 1024)) break; + + if ((!(*buf)) || + ((buf[0] >= '0') && (buf[0] <= '9')) || (buf[0] == '#')) continue; + + if (buf[1] != ' ') + lens[get_key(buf[1])]++; + } + + /* Open the file */ + txt = my_fopen(newname, "w"); + + /* Open the file */ + fff = my_fopen(base, "r"); + + /* Oops */ + if (!fff || !txt) + { + my_fclose(fff); + my_fclose(txt); + + /* Oops */ + return (TRUE); + } + + for (i = 0; i < KEY_NUM; i++) lens[i] = 0; + + /* Allocate big amount of temporary storage */ + C_MAKE(strs, KEY_NUM, chg_type); + + /* Display the file */ + while (TRUE) + { + /* Skip a line */ + if (my_fgets(fff, buf, 1024)) break; + + if ((!(*buf)) || + ((buf[0] >= '0') && (buf[0] <= '9')) || (buf[0] == '#')) continue; + + if (buf[1] != ' ') key = get_key(buf[1]); + + if (key == KEY_NUM - 1) + strcpy(strs[key][lens[key]++], buf + 5); + else + strcpy(strs[key][lens[key]++], buf + 3); + } + + fprintf(txt, "%s changes\n", strs[KEY_NUM - 1][0]); + + for (i = 0; i < KEY_NUM - 1; i++) + { + if (lens[i]) fprintf(txt, "\n%s\n", keys_desc[i]); + + for (j = 0; j < lens[i]; j++) + { + fprintf(txt, "%s\n", strs[i][j]); + } + } + + /* Close the file */ + my_fclose(txt); + my_fclose(fff); + + /* Free temporary memory */ + C_FREE(strs, KEY_NUM, chg_type); + + /* Normal return */ + return (TRUE); +} + +#endif /* !WINDOWS && !MACINTOSH && !ACORN */ + +/* + * 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. + * Extract a clean "base name". + * Build the savefile name if needed. + */ +void process_player_base() +{ + char temp[128]; + +#if defined(SAVEFILE_USE_UID) && !defined(PRIVATE_USER_PATH) + /* Rename the savefile, using the player_uid and player_base */ + (void)sprintf(temp, "%d.%s", player_uid, player_base); +#else + /* Rename the savefile, using the player_base */ + (void)sprintf(temp, "%s", player_base); +#endif + +#ifdef VM + /* Hack -- support "flat directory" usage on VM/ESA */ + (void)sprintf(temp, "%s.sv", player_base); +#endif /* VM */ + + /* Build the filename */ + path_build(savefile, 1024, ANGBAND_DIR_SAVE, temp); +} + +void process_player_name(bool sf) +{ + int i, k = 0; + char tmp[50]; + + /* Cannot be too long */ + if (strlen(player_base) > 15) + { + /* Name too long */ + quit_fmt("The name '%s' is too long!", player_base); + } + + /* Cannot contain "icky" characters */ + for (i = 0; player_base[i]; i++) + { + /* No control characters */ + if (iscntrl(player_base[i])) + { + /* Illegal characters */ + quit_fmt("The name '%s' contains control chars!", player_base); + } + } + + +#ifdef MACINTOSH + + /* Extract "useful" letters */ + for (i = 0; player_base[i]; i++) + { + char c = player_base[i]; + + /* Convert "dot" to "underscore" */ + if (c == '@.') c = '_'; + + /* Accept all the letters */ + tmp[k++] = c; + } + +#else + + /* Extract "useful" letters */ + for (i = 0; player_base[i]; i++) + { + char c = player_base[i]; + + /* Accept some letters */ + if (isalpha(c) || isdigit(c)) tmp[k++] = c; + + /* Convert space, dot, and underscore to underscore */ + else if (strchr("@. _", c)) tmp[k++] = '_'; + } + +#endif + + +#if defined(WINDOWS) || defined(MSDOS) + + /* Hack -- max length */ + if (k > 8) k = 8; + +#endif + + /* Terminate */ + tmp[k] = '\0'; + sprintf(player_base, tmp); + + /* Require a "base" name */ + if (!player_base[0]) strcpy(player_base, "PLAYER"); + + +#ifdef SAVEFILE_MUTABLE + + /* Accept */ + sf = TRUE; + +#endif + + /* Change the savefile name */ + if (sf) + { + process_player_base(); + } +} + + +/* + * Gets a name for the character, reacting to name changes. + * + * Assumes that "display_player(0)" has just been called + * + * Perhaps we should NOT ask for a name (at "birth()") on + * Unix machines? XXX XXX + * + * What a horrible name for a global function. XXX XXX XXX + */ +void get_name(void) +{ + char tmp[32]; + + /* Clear last line */ + clear_from(22); + + /* Prompt and ask */ + prt("[Enter your player's name above, or hit ESCAPE]", 23, 2); + + /* Ask until happy */ + while (1) + { + /* Go to the "name" field */ + move_cursor(2, 9); + + /* Save the player name */ + strcpy(tmp, player_name); + + /* Get an input, ignore "Escape" */ + if (askfor_aux(tmp, 31)) strcpy(player_name, tmp); + + /* Process the player name */ + process_player_name(FALSE); + + /* All done */ + break; + } + + /* Pad the name (to clear junk) */ + sprintf(tmp, "%-31.31s", player_name); + + /* Re-Draw the name (in light blue) */ + c_put_str(TERM_L_BLUE, tmp, 2, 9); + + /* Erase the prompt, etc */ + clear_from(22); +} + + + +/* + * Hack -- commit suicide + */ +void do_cmd_suicide(void) +{ + int i; + + /* Flush input */ + flush(); + + /* Verify Retirement */ + if (total_winner) + { + /* Verify */ + if (!get_check("Do you want to retire? ")) return; + } + + /* Verify Suicide */ + else + { + /* Verify */ + if (!get_check("Do you really want to quit? ")) return; + + if (!noscore) + { + /* Special Verification for suicide */ + prt("Please verify QUITTING by typing the '@' sign: ", 0, 0); + flush(); + i = inkey(); + prt("", 0, 0); + if (i != '@') return; + } + } + + /* Stop playing */ + alive = FALSE; + + /* Kill the player */ + death = TRUE; + + /* Leaving */ + p_ptr->leaving = TRUE; + + /* Cause of death */ + (void)strcpy(died_from, "Quitting"); +} + + + /* HACK - Remove / set the CAVE_VIEW flag, since view_x / view_y + * is not saved, and the visible locations are not lighted correctly + * when the game is loaded again + * Alternatively forget_view() and update_view() can be used + */ +void remove_cave_view(bool remove) +{ + int i; + cave_type *c_ptr; + + if (view_n) + { + /* Clear them all */ + for (i = 0; i < view_n; i++) + { + int y = view_y[i]; + int x = view_x[i]; + + /* Access the grid */ + c_ptr = &cave[y][x]; + + if (remove) + c_ptr->info &= ~(CAVE_VIEW); + else + c_ptr->info |= (CAVE_VIEW); + } + } +} + +/* + * Save the game + */ +void do_cmd_save_game(void) +{ + panic_save = 0; /* Fixes an apparently long-lived bug */ + + remove_cave_view(TRUE); + + /* Save the current level if in a persistent level */ + save_dungeon(); + + /* Autosaves do not disturb */ + if (!is_autosave) + { + /* 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 */ + (void)strcpy(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); + } + + remove_cave_view(FALSE); + + /* Allow suspend again */ + signals_handle_tstp(); + + /* Refresh */ + Term_fresh(); + + /* Note that the player is not dead */ + (void)strcpy(died_from, "(alive and well)"); +} + + + +/* + * Hack -- Calculates the total number of points earned -JWT- + */ +long total_points(void) +{ +#if 0 /* Old calculation */ + s16b max_dl = 0, i; + + for (i = 0; i < max_d_idx; i++) + if (max_dlv[i] > max_dl) + max_dl = max_dlv[i]; + + return (p_ptr->max_exp + (100 * max_dl)); +#else /* New calculation */ + s16b max_dl = 0, i, k; + long temp, Total = 0; + long mult = 20; /* was 100. Divided values by 5 because of an overflow error */ + long comp_death = (p_ptr->companion_killed * 2 / 5); + + if (!comp_death) comp_death = 1; + + if (p_ptr->preserve) mult -= 1; /* Penalize preserve, maximize modes */ + if (p_ptr->maximize) mult -= 1; + if (auto_scum) mult -= 4; + if (stupid_monsters) mult -= 10; + if (small_levels) mult += ((always_small_level) ? 4 : 10); + if (empty_levels) mult += 2; + if (smart_learn) mult += 4; + if (smart_cheat) mult += 4; + + if (mult < 2) mult = 2; /* At least 10% of the original score */ + /* mult is now between 2 and 40, i.e. 10% and 200% */ + + for (i = 0; i < max_d_idx; i++) + if (max_dlv[i] > max_dl) + max_dl = max_dlv[i]; + + temp = p_ptr->lev * p_ptr->lev * p_ptr->lev * p_ptr->lev + (100 * max_dl); + + temp += p_ptr->max_exp / 5; + + temp = (temp * mult / 20); + + /* Gold increases score */ + temp += p_ptr->au / 5; + + /* Completing quest increase score */ + for (i = 0; i < max_q_idx; i++) + { + if (quest[i].status >= QUEST_STATUS_COMPLETED) + { + temp += 2000; + temp += quest[i].level * 100; + } + } + + /* Death of a companion is BAD */ + temp /= comp_death; + + /* The know objects increase the score */ + /* Scan the object kinds */ + for (k = 1; k < max_k_idx; k++) + { + object_kind *k_ptr = &k_info[k]; + + /* Hack -- skip artifacts */ + if (k_ptr->flags3 & (TR3_INSTA_ART)) continue; + + /* List known flavored objects */ + if (k_ptr->flavor && k_ptr->aware) + { + object_type *i_ptr; + object_type object_type_body; + + /* Get local object */ + i_ptr = &object_type_body; + + /* Create fake object */ + object_prep(i_ptr, k); + + temp += object_value_real(i_ptr); + } + } + + for (k = 1; k < max_r_idx; k++) + { + monster_race *r_ptr = &r_info[k]; + + if (r_ptr->flags1 & (RF1_UNIQUE)) + { + bool dead = (r_ptr->max_num == 0); + + if (dead) + { + /* Uniques are supposed to be harder */ + Total += 50; + } + } + else + { + s16b This = r_ptr->r_pkills; + + if (This > 0) + { + Total += This; + } + } + } + temp += Total * 50; + + temp += total_bounties * 100; + + if (total_winner) temp += 1000000; + + + + return (temp); +#endif +} + + + +/* + * Centers a string within a 31 character string -JWT- + */ +static void center_string(char *buf, cptr str) +{ + int i, j; + + /* Total length */ + i = strlen(str); + + /* Necessary border */ + j = 15 - i / 2; + + /* Mega-Hack */ + (void)sprintf(buf, "%*s%s%*s", j, "", str, 31 - i - j, ""); +} + + +/* + * Redefinable "print_tombstone" action + */ +bool (*tombstone_aux)(void) = NULL; + + +/* + * Display a "tomb-stone" + */ +static void print_tomb(void) +{ + bool done = FALSE; + + /* Do we use a special tombstone ? */ + if (tombstone_aux) + { + /* Use tombstone hook */ + done = (*tombstone_aux)(); + } + + /* Print the text-tombstone */ + if (!done) + { + cptr p; + + char tmp[160]; + + char buf[1024]; + char dummy[80]; + + 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"); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the News file */ + fp = my_fopen(buf, "r"); + + /* Drop permission */ + safe_setuid_drop(); + + /* Dump */ + if (fp) + { + int i = 0; + + /* Dump the file to the screen */ + while (0 == my_fgets(fp, buf, 1024)) + { + /* Display and advance */ + display_message(0, i++, strlen(buf), TERM_WHITE, buf); + } + + /* Close */ + my_fclose(fp); + } + + + /* King or Queen */ + if (total_winner || (p_ptr->lev > PY_MAX_LEVEL)) + { + p = "Magnificent"; + } + + /* Normal */ + else + { + p = cp_ptr->titles[(p_ptr->lev - 1) / 5] + c_text; + } + + center_string(buf, player_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, spp_ptr->title + c_name); + put_str(buf, 10, 11); + + (void)sprintf(tmp, "Level: %d", (int)p_ptr->lev); + center_string(buf, tmp); + put_str(buf, 11, 11); + + (void)sprintf(tmp, "Exp: %ld", (long)p_ptr->exp); + center_string(buf, tmp); + put_str(buf, 12, 11); + + (void)sprintf(tmp, "AU: %ld", (long)p_ptr->au); + center_string(buf, tmp); + put_str(buf, 13, 11); + + (void)sprintf(tmp, "Killed on Level %d", dun_level); + center_string(buf, tmp); + put_str(buf, 14, 11); + + + if (strlen(died_from) > 24) + { + strncpy(dummy, died_from, 24); + dummy[24] = '\0'; + (void)sprintf(tmp, "by %s.", dummy); + } + else + (void)sprintf(tmp, "by %s.", died_from); + + center_string(buf, tmp); + put_str(buf, 15, 11); + + + (void)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; + + /* Hack -- Know everything in the inven/equip */ + for (i = 0; i < INVEN_TOTAL; i++) + { + o_ptr = &p_ptr->inventory[i]; + + /* Skip non-objects */ + if (!o_ptr->k_idx) continue; + + /* Aware and Known */ + object_aware(o_ptr); + object_known(o_ptr); + } + + for (i = 1; i < max_towns; i++) + { + st_ptr = &town_info[i].store[7]; + + /* Hack -- Know everything in the home */ + for (j = 0; j < st_ptr->stock_num; j++) + { + o_ptr = &st_ptr->stock[j]; + + /* 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 out_val[160]; + + /* Prompt */ + put_str("Filename(you can post it to http://angband.oook.cz/): ", 23, 0); + + /* Default */ + strcpy(out_val, ""); + + /* Ask for filename (or abort) */ + if (!askfor_aux(out_val, 60)) return; + + /* Return means "show on screen" */ + if (!out_val[0]) break; + + /* Save screen */ + character_icky = TRUE; + Term_save(); + + /* Dump a character file */ + (void)file_character(out_val, TRUE); + + /* Load screen */ + Term_load(); + character_icky = FALSE; + } + + + /* Display player */ + display_player(0); + + /* Prompt for p_ptr->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 (equip_cnt) + { + Term_clear(); + item_tester_full = TRUE; + show_equip(FALSE); + prt("You are using: -more-", 0, 0); + if (inkey() == ESCAPE) return; + } + + /* Inventory -- if any */ + if (inven_cnt) + { + Term_clear(); + item_tester_full = TRUE; + show_inven(FALSE); + prt("You are carrying: -more-", 0, 0); + if (inkey() == ESCAPE) return; + } + + /* Homes in the different towns */ + for (k = 1; k < max_towns; k++) + { + st_ptr = &town_info[k].store[7]; + +#if 0 /* TODO -- actualy somewhere set the real variable */ + /* Step to the real towns */ + if (!(town_info[k].flags & (TOWN_REAL))) continue; +#endif + + /* 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++) + { + char o_name[80]; + char tmp_val[80]; + + /* Acquire item */ + o_ptr = &st_ptr->stock[i]; + + /* Print header, clear line */ + sprintf(tmp_val, "%c) ", I2A(j)); + prt(tmp_val, j + 2, 4); + + /* Display object description */ + object_desc(o_name, o_ptr, TRUE, 3); + c_put_str(tval_to_attr[o_ptr->tval], o_name, j + 2, 7); + } + + /* h_ptr->caption */ + prt(format("Your home contains (page %d): -more-", k + 1), 0, 0); + + /* Wait for it */ + if (inkey() == ESCAPE) return; + } + } + } +} + + + + + +/* + * Semi-Portable High Score List Entry (128 bytes) -- BEN + * + * All fields listed below are null terminated ascii strings. + * + * In addition, the "number" fields are right justified, and + * space padded, to the full available length (minus the "null"). + * + * Note that "string comparisons" are thus valid on "pts". + */ + +typedef struct high_score high_score; + +struct high_score +{ + char what[8]; /* Version info (string) */ + + char pts[10]; /* Total Score (number) */ + + char gold[10]; /* Total Gold (number) */ + + char turns[10]; /* Turns Taken (number) */ + + char day[10]; /* Time stamp (string) */ + + char who[16]; /* Player Name (string) */ + + char uid[8]; /* Player UID (number) */ + + char sex[2]; /* Player Sex (string) */ + char p_r[3]; /* Player Race (number) */ + char p_s[3]; /* Player Subrace (number) */ + char p_c[3]; /* Player Class (number) */ + char p_cs[3]; /* Player Class spec (number) */ + + char cur_lev[4]; /* Current Player Level (number) */ + char cur_dun[4]; /* Current Dungeon Level (number) */ + char max_lev[4]; /* Max Player Level (number) */ + char max_dun[4]; /* Max Dungeon Level (number) */ + + char arena_number[4]; /* Arena level attained -KMW- */ + char inside_arena[4]; /* Did the player die in the arena? */ + char inside_quest[4]; /* Did the player die in a quest? */ + char exit_bldg[4]; /* Can the player exit arena? Goal obtained? -KMW- */ + + char how[32]; /* Method of death (string) */ +}; + + + +/* + * 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. + */ +static void display_scores_aux(int from, int to, int note, high_score *score) +{ + int i, j, k, n, place; + byte attr; + char out_val[256]; + char tmp_val[160]; + high_score the_score; + + + /* 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(format(" %s Hall of Fame", game_module), 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 pcs, pr, ps, pc, clev, mlev, cdun, mdun; + + cptr user, gold, when, aged; + + int in_arena, in_quest; + + /* 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); + ps = atoi(the_score.p_s); + pc = atoi(the_score.p_c); + pcs = atoi(the_score.p_cs); + + /* 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); + + in_arena = atoi(the_score.inside_arena); + in_quest = atoi(the_score.inside_quest); + + /* 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, + get_player_race_name(pr, ps), class_info[pc].spec[pcs].title + c_name, + 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 */ + if (in_arena) + { + sprintf(out_val, " Killed by %s in the Arena", + the_score.how); + } + else if (in_quest) + { + sprintf(out_val, " Killed by %s while questing", + the_score.how); + } + /* Hack -- some people die in the town */ + else if (!cdun) + { + sprintf(out_val, " Killed by %s in the Town", + the_score.how); + } + else + { + sprintf(out_val, " Killed by %s on %s %d", + the_score.how, "Dungeon Level", cdun); + } + + /* 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); + j = inkey(); + prt("", 23, 0); + + /* Hack -- notice Escape */ + if (j == 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"); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the binary high score file, for reading */ + highscore_fd = fd_open(buf, O_RDONLY); + + /* Drop permission */ + safe_setuid_drop(); + + /* Paranoia -- No score file */ + if (highscore_fd < 0) quit("Score file unavailable."); + + /* Clear screen */ + Term_clear(); + + /* Display the scores */ + display_scores_aux(from, to, -1, NULL); + + /* Shut the high score file */ + (void)fd_close(highscore_fd); + + /* Forget the high score fd */ + highscore_fd = -1; + + /* Quit */ + quit(NULL); +} + + +/* + * show_highclass - selectively list highscores based on class + * -KMW- + */ +void show_highclass(int building) +{ + + register int i = 0, j, m = 0; + int pcs, pr, ps, pc, clev, al; + high_score the_score; + char buf[1024], out_val[256]; + + switch (building) + { + case 1: + prt(" Busts of Greatest Kings", 5, 0); + break; + case 2: + prt(" Plaque - Greatest Arena Champions", 5, 0); + break; + case 10: + prt(" Plaque - Greatest Fighters", 5, 0); + break; + case 11: + prt(" Spires of the Greatest Magic-Users", 5, 0); + break; + case 12: + prt(" Busts of Greatest Priests", 5, 0); + break; + case 13: + prt(" Wall Inscriptions - Greatest Thieves", 5, 0); + break; + case 14: + prt(" Plaque - Greatest Rangers", 5, 0); + break; + case 15: + prt(" Plaque - Greatest Paladins", 5, 0); + break; + case 16: + prt(" Spires of the Greatest Illusionists", 5, 0); + break; + default: + bell(); + break; + } + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw"); + + /* Grab permission */ + safe_setuid_grab(); + + highscore_fd = fd_open(buf, O_RDONLY); + + /* Drop permission */ + safe_setuid_drop(); + + if (highscore_fd < 0) + { + msg_print("Score file unavailable."); + msg_print(NULL); + return; + } + + if (highscore_seek(0)) return; + + for (i = 0; i < MAX_HISCORES; i++) + if (highscore_read(&the_score)) break; + + m = 0; + j = 0; + clev = 0; + + while ((m < 9) || (j < MAX_HISCORES)) + { + if (highscore_seek(j)) break; + if (highscore_read(&the_score)) break; + pr = atoi(the_score.p_r); + ps = atoi(the_score.p_s); + pc = atoi(the_score.p_c); + pcs = atoi(the_score.p_cs); + clev = atoi(the_score.cur_lev); + al = atoi(the_score.arena_number); + if (((pc == (building - 10)) && (building != 1) && (building != 2)) || + ((building == 1) && (clev >= PY_MAX_LEVEL)) || + ((building == 2) && (al > MAX_ARENA_MONS))) + { + sprintf(out_val, "%3d) %s the %s (Level %2d)", + (m + 1), the_score.who, rp_name + race_info[pr].title, clev); + prt(out_val, (m + 7), 0); + m++; + } + j++; + } + + /* Now, list the active player if they qualify */ + if ((building == 1) && (p_ptr->lev >= PY_MAX_LEVEL)) + { + sprintf(out_val, "You) %s the %s (Level %2d)", + player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev); + prt(out_val, (m + 8), 0); + } + else if ((building == 2) && (p_ptr->arena_number > MAX_ARENA_MONS)) + { + sprintf(out_val, "You) %s the %s (Level %2d)", + player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev); + prt(out_val, (m + 8), 0); + } + else if ((building != 1) && (building != 2)) + { + if ((p_ptr->lev > clev) && (p_ptr->pclass == (building - 10))) + { + sprintf(out_val, "You) %s the %s (Level %2d)", + player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev); + prt(out_val, (m + 8), 0); + } + } + + (void)fd_close(highscore_fd); + highscore_fd = -1; + msg_print("Hit any key to continue"); + msg_print(NULL); + for (j = 5; j < 18; j++) + prt("", j, 0); +} + + +/* + * Race Legends + * -KMW- + */ +void race_score(int race_num) +{ + register int i = 0, j, m = 0; + int pr, ps, pc, clev, pcs, al, lastlev; + high_score the_score; + char buf[1024], out_val[256], tmp_str[80]; + + lastlev = 0; + + /* rr9: TODO - pluralize the race */ + sprintf(tmp_str, "The Greatest of all the %s", rp_name + race_info[race_num].title); + prt(tmp_str, 5, 3); + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw"); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the highscore file */ + highscore_fd = fd_open(buf, O_RDONLY); + + /* Drop permission */ + safe_setuid_drop(); + + if (highscore_fd < 0) + { + msg_print("Score file unavailable."); + msg_print(NULL); + return; + } + + if (highscore_seek(0)) return; + + for (i = 0; i < MAX_HISCORES; i++) + { + if (highscore_read(&the_score)) break; + } + + m = 0; + j = 0; + + while ((m < 10) && (j < i)) + { + if (highscore_seek(j)) break; + if (highscore_read(&the_score)) break; + pr = atoi(the_score.p_r); + ps = atoi(the_score.p_s); + pc = atoi(the_score.p_c); + pcs = atoi(the_score.p_cs); + clev = atoi(the_score.cur_lev); + al = atoi(the_score.arena_number); + if (pr == race_num) + { + sprintf(out_val, "%3d) %s the %s (Level %3d)", + (m + 1), the_score.who, + rp_name + race_info[pr].title, clev); + prt(out_val, (m + 7), 0); + m++; + lastlev = clev; + } + j++; + } + + /* add player if qualified */ + if ((p_ptr->prace == race_num) && (p_ptr->lev >= lastlev)) + { + sprintf(out_val, "You) %s the %s (Level %3d)", + player_name, rp_name + race_info[p_ptr->prace].title, p_ptr->lev); + prt(out_val, (m + 8), 0); + } + + (void)fd_close(highscore_fd); + highscore_fd = -1; +} + + +/* + * Race Legends + * -KMW- + */ +void race_legends(void) +{ + int i, j; + + for (i = 0; i < max_rp_idx; i++) + { + race_score(i); + msg_print("Hit any key to continue"); + msg_print(NULL); + for (j = 5; j < 19; j++) + prt("", j, 0); + } +} + + + + +/* + * 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, *tmp; + + 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 (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 (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 */ + if (noscore & 0xFF00) + { + msg_print("Score not registered for cheaters."); + msg_print(NULL); + display_scores_aux(0, 10, -1, NULL); + return (0); + } +#endif + + /* Interupted */ + if (!total_winner && streq(died_from, "Interrupting")) + { + msg_print("Score not registered due to interruption."); + msg_print(NULL); + display_scores_aux(0, 10, -1, NULL); + return (0); + } + + /* Quitter */ + if (!total_winner && streq(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 */ + tmp = WIPE(&the_score, high_score); + + /* Save the version */ + sprintf(the_score.what, "%lu.%lu.%lu", + 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 - (START_DAY * 10L)); + 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", player_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_s, "%2d", p_ptr->pracem); + sprintf(the_score.p_c, "%2d", p_ptr->pclass); + sprintf(the_score.p_cs, "%2d", p_ptr->pspec); + + /* Save the level and such */ + sprintf(the_score.cur_lev, "%3d", p_ptr->lev); + sprintf(the_score.cur_dun, "%3d", dun_level); + sprintf(the_score.max_lev, "%3d", p_ptr->max_plv); + sprintf(the_score.max_dun, "%3d", max_dlv[dungeon_type]); + + sprintf(the_score.arena_number, "%3d", p_ptr->arena_number); /* -KMW- */ + sprintf(the_score.inside_arena, "%3d", p_ptr->inside_arena); + sprintf(the_score.inside_quest, "%3d", p_ptr->inside_quest); + sprintf(the_score.exit_bldg, "%3d", p_ptr->exit_bldg); /* -KMW- */ + + /* Save the cause of death (31 chars) */ + sprintf(the_score.how, "%-.31s", 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. + */ +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, "%lu.%lu.%lu", + 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 - (START_DAY * 10L)); + the_score.turns[9] = '\0'; + + /* Hack -- no time needed */ + strcpy(the_score.day, "TODAY"); + + /* Save the player name (15 chars) */ + sprintf(the_score.who, "%-.15s", player_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_s, "%2d", p_ptr->pracem); + sprintf(the_score.p_c, "%2d", p_ptr->pclass); + sprintf(the_score.p_cs, "%2d", p_ptr->pspec); + + /* Save the level and such */ + sprintf(the_score.cur_lev, "%3d", p_ptr->lev); + sprintf(the_score.cur_dun, "%3d", dun_level); + sprintf(the_score.max_lev, "%3d", p_ptr->max_plv); + sprintf(the_score.max_dun, "%3d", max_dlv[dungeon_type]); + + sprintf(the_score.arena_number, "%3d", p_ptr->arena_number); /* -KMW- */ + sprintf(the_score.inside_arena, "%3d", p_ptr->inside_arena); + sprintf(the_score.inside_quest, "%3d", p_ptr->inside_quest); + sprintf(the_score.exit_bldg, "%3d", p_ptr->exit_bldg); /* -KMW- */ + + /* 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 King! -RAK- + */ +static void kingly(void) +{ + /* Hack -- retire in town */ + dun_level = 0; + + /* Fake death */ + (void)strcpy(died_from, "Ripe Old Age"); + + /* Restore the experience */ + p_ptr->exp = p_ptr->max_exp; + + /* Restore the level */ + p_ptr->lev = p_ptr->max_plv; + + /* Hack -- Instant Gold */ + p_ptr->au += 10000000L; + + /* Clear screen */ + Term_clear(); + + /* Would like to see something more Tolkienian here... */ + + /* 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); +} + + +/* + * Wipe the saved levels + */ +void wipe_saved() +{ + int d, l, od = dungeon_type, ol = dun_level; + + for (d = 0; d < max_d_idx; d++) + { + dungeon_info_type *d_ptr = &d_info[d]; + + for (l = d_ptr->mindepth; l <= d_ptr->maxdepth; l++) + { + char buf[10]; + + dun_level = l; + dungeon_type = d; + if (get_dungeon_save(buf)) + { + char tmp[80], name[1024]; + + sprintf(tmp, "%s.%s", player_base, buf); + path_build(name, 1024, ANGBAND_DIR_SAVE, tmp); + + /* Grab permission */ + safe_setuid_grab(); + + /* Remove the dungeon save file */ + fd_kill(name); + + /* Drop permission */ + safe_setuid_drop(); + } + } + } + + dungeon_type = od; + dun_level = ol; +} + + +/* + * Close up the current game (player may or may not be dead) + * + * This function is called only from "main.c" and "signals.c". + */ +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 -- Character is now "icky" */ + character_icky = TRUE; + + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw"); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the high score file, for reading/writing */ + highscore_fd = fd_open(buf, O_RDWR); + + /* Drop permission */ + safe_setuid_drop(); + + /* Handle death */ + if (death) + { + /* Handle retirement */ + if (total_winner) + { + /* Write a note, if that option is on */ + if (take_notes) + { + add_note_type(NOTE_WINNER); + } + + irc_disconnect_aux(format("Retired; %s %ld.%ld.%ld rules", + game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH), FALSE); + kingly(); + } + else + { + irc_disconnect_aux(format("Killed by %s; %s %ld.%ld.%ld rules", + died_from, game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH), + FALSE); + } + + /* Wipe the saved levels */ + wipe_saved(); + + /* Save memories */ + if (!save_player()) msg_print("Death save failed!"); + + /* You are dead */ + print_tomb(); + + /* Show more info */ + show_info(); + + /* Write a note */ + if (take_notes) + { + char long_day[30]; + char buf[80]; + time_t ct = time((time_t*)NULL); + + /* Get the date */ + strftime(long_day, 30, + "%Y-%m-%d at %H:%M:%S", localtime(&ct)); + + /* Create string */ + sprintf(buf, "\n%s was killed by %s on %s\n", player_name, + died_from, long_day); + + /* Output to the notes file */ + output_note(buf); + } + + /* Dump bones file */ + make_bones(); + + /* Handle score, show Top scores */ + top_twenty(); + } + + /* Still alive */ + else + { + is_autosave = FALSE; + + /* Save the game */ + do_cmd_save_game(); + + /* If note-taking enabled, write session end to notes file */ + if (take_notes) + { + add_note_type(NOTE_SAVE_GAME); + } + + irc_disconnect_aux(format("Alive... for the time being; %s %ld.%ld.%ld rules", + game_module, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH), + FALSE); + + /* 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 */ + (void)fd_close(highscore_fd); + + /* Forget the high score fd */ + highscore_fd = -1; + + /* 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); + + /* Mega-Hack -- Delay death */ + if (p_ptr->chp < 0) death = FALSE; + + /* Hardcode panic save */ + panic_save = 1; + + /* Forbid suspend */ + signals_ignore_tstp(); + + /* Indicate panic save */ + (void)strcpy(died_from, "(panic save)"); + + /* Panic save, or get worried */ + if (!save_player()) quit("panic save failed!"); + + /* Successful panic save */ + quit("panic save succeeded!"); +} + + +/* + * Grab a randomly selected line in lib/file/file_name + */ +errr get_rnd_line(char *file_name, char *output) +{ + FILE *fp; + + char buf[1024]; + + int lines = 0; + + int line; + + int i; + + + /* Clear the output buffer */ + strcpy(output, ""); + + /* test hack */ + if (wizard && cheat_xtra) msg_print(file_name); + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_FILE, file_name); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the file */ + fp = my_fopen(buf, "r"); + + /* Drop permission */ + safe_setuid_drop(); + + /* Failed */ + if (!fp) return ( -1); + + /* Read the first line */ + if (0 != my_fgets(fp, buf, 80)) + { + my_fclose(fp); + return ( -1); + } + + /* Retrieve number of valid lines in the file */ + lines = atoi(buf); + + /* Pick a line in the file */ + line = randint(lines); + + /* + * Scan through the file XXX XXX XXX + * Seemingly wrong use of the counter is justified by the + * stupid 'buffer' lines in the random text files. + */ + for (i = 0; i <= line; i++) + { + if (0 != my_fgets(fp, buf, 80)) + { + my_fclose(fp); + return ( -1); + } + + /* Found the line */ + if (i == line) break; + } + + /* Copy the line to the output buffer */ + strcpy(output, buf); + + /* Close the file */ + my_fclose(fp); + + /* Success */ + return (0); +} + + +/* + * Read line'th line file the file + * and return pointer to it, or NULL if it fails. + * + * Nuked the static buffer. Caller should provide one. -- pelpel + * + * Caution: 'linbuf' should be at least 80 byte long. + */ +char *get_line(char* fname, cptr fdir, char *linbuf, int line) +{ + FILE* fp; + int i; + char buf[1024]; + + + /* Don't count the first line in the file, which is a comment line */ + line++; + + /* Build the filename */ + path_build(buf, 1024, fdir, fname); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the file */ + fp = my_fopen(buf, "r"); + + /* Drop permission */ + safe_setuid_drop(); + + /* Failed */ + if (!fp) return (NULL); + + /* Read past specified number of lines */ + for (i = 0; i <= line; i++) + { + /* Oops */ + if (my_fgets(fp, linbuf, 80) != 0) + { + my_fclose(fp); + return (NULL); + } + } + + my_fclose(fp); + + return (linbuf); +} + + +/* + * Return a line for a speaking unique, by Matt G. + * + * XXX XXX XXX Opening a file and scanning it through whenever a unique + * tries to say something? Something like DELAY_LOAD_?_TEXT would be + * much better -- pelpel + * + * XXX XXX XXX I must say the original is an extremely poor and unreliable + * implementation... I removed noxious flag -- I'm too stupid to + * understand such complexities -- and added extra error checkings + * and made sure fd is always closed -- pelpel + */ +errr get_xtra_line(char *file_name, monster_type *m_ptr, char *output) +{ + FILE *fp; + char buf[1024]; + int line; + int num_entries; + int i; + int mnum; + + + /* Clear the message buffer */ + strcpy(output, ""); + + /* test and DEBUG hack */ + if (wizard && cheat_xtra) + { + msg_print(file_name); + } + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_FILE, file_name); + + /* Grab permission */ + safe_setuid_grab(); + + /* Open the file */ + fp = my_fopen(buf, "r"); + + /* Drop permission */ + safe_setuid_drop(); + + /* Failed */ + if (!fp) return ( -1); + + /* Monster number we are looking for */ + mnum = m_ptr->r_idx; + + /* Find matching N: line */ + while (1) + { + int n; + + /* Read a line */ + if (my_fgets(fp, buf, 90) != 0) + { + my_fclose(fp); + return ( -1); + } + + /* Not a N: line */ + if (buf[0] != 'N') continue; + + /* Skip "N:" and parse off a number */ + sscanf(buf + 2, "%d", &n); + + /* Match found */ + if (n == mnum) break; + } + + /* Retrieve number of normal messages */ + while (1) + { + /* Read next line */ + if (my_fgets(fp, buf, 90) != 0) + { + my_fclose(fp); + return ( -1); + } + + /* The first line not beginning with 'N:' holds number of lines */ + if (buf[0] != 'N') + { + num_entries = atoi(buf); + break; + } + } + + /* The monster is afraid */ + if (m_ptr->monfear) + { + /* Read past normal lines */ + for (line = 0; line < num_entries + 1; line++) + { + if (my_fgets(fp, buf, 90)) + { + my_fclose(fp); + return ( -1); + } + } + + /* Retrieve number of 'afraid' lines */ + num_entries = atoi(buf); + } + + + /* Pick a random line */ + line = rand_int(num_entries); + + /* test and DEBUG hack */ + if (wizard && cheat_xtra) + { + sprintf(buf, "Line number %d", line); + msg_print(buf); + } + + /* Find the selected line */ + for (i = 0; i <= line; i++) + { + /* Oops */ + if (0 != my_fgets(fp, buf, 90)) + { + my_fclose(fp); + return ( -1); + } + + /* Found it */ + if (i == line) break; + } + + /* Copy it to the output buffer */ + strcpy(output, buf); + + /* Close the file */ + my_fclose(fp); + + /* Success */ + return (0); +} + + +#ifdef HANDLE_SIGNALS + + +#include <signal.h> + + +/* + * 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 (death) + { + /* Mark the savefile */ + (void)strcpy(died_from, "Abortion"); + + /* Close stuff */ + close_game(); + + /* Quit */ + quit("interrupt"); + } + + /* Allow suicide (after 5) */ + else if (signal_count >= 5) + { + /* Cause of "death" */ + (void)strcpy(died_from, "Interrupting"); + + /* Stop playing */ + alive = FALSE; + + /* Suicide */ + death = TRUE; + + /* 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 */ + panic_save = 1; + + /* Panic save */ + (void)strcpy(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(format("software bug %d %d", p_ptr->px, p_ptr->py)); +} + + + + +/* + * 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 */ |