Angband 3.0.9b.
[angband.git] / src / files.c
blob35d4f55d620d45ab15b2d80828043be108265d8f
1 /* File: files.c */
3 /*
4 * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6 * This software may be copied and distributed for educational, research,
7 * and not for profit purposes provided that this copyright and statement
8 * are included in all such copies. Other copyrights may also apply.
9 */
11 #include "angband.h"
12 #include "cmds.h"
14 #define MAX_PANEL 12
17 #if 0
20 * Use this (perhaps) for Angband 2.8.4
22 * Extract "tokens" from a buffer
24 * This function uses "whitespace" as delimiters, and treats any amount of
25 * whitespace as a single delimiter. We will never return any empty tokens.
26 * When given an empty buffer, or a buffer containing only "whitespace", we
27 * will return no tokens. We will never extract more than "num" tokens.
29 * By running a token through the "text_to_ascii()" function, you can allow
30 * that token to include (encoded) whitespace, using "\s" to encode spaces.
32 * We save pointers to the tokens in "tokens", and return the number found.
34 static s16b tokenize_whitespace(char *buf, s16b num, char **tokens)
36 int k = 0;
38 char *s = buf;
41 /* Process */
42 while (k < num)
44 char *t;
46 /* Skip leading whitespace */
47 for ( ; *s && isspace((unsigned char)*s); ++s) /* loop */;
49 /* All done */
50 if (!*s) break;
52 /* Find next whitespace, if any */
53 for (t = s; *t && !isspace((unsigned char)*t); ++t) /* loop */;
55 /* Nuke and advance (if necessary) */
56 if (*t) *t++ = '\0';
58 /* Save the token */
59 tokens[k++] = s;
61 /* Advance */
62 s = t;
65 /* Count */
66 return (k);
69 #endif
73 * Extract the first few "tokens" from a buffer
75 * This function uses "colon" and "slash" as the delimeter characters.
77 * We never extract more than "num" tokens. The "last" token may include
78 * "delimeter" characters, allowing the buffer to include a "string" token.
80 * We save pointers to the tokens in "tokens", and return the number found.
82 * Hack -- Attempt to handle the 'c' character formalism
84 * Hack -- An empty buffer, or a final delimeter, yields an "empty" token.
86 * Hack -- We will always extract at least one token
88 s16b tokenize(char *buf, s16b num, char **tokens)
90 int i = 0;
92 char *s = buf;
95 /* Process */
96 while (i < num - 1)
98 char *t;
100 /* Scan the string */
101 for (t = s; *t; t++)
103 /* Found a delimiter */
104 if ((*t == ':') || (*t == '/')) break;
106 /* Handle single quotes */
107 if (*t == '\'')
109 /* Advance */
110 t++;
112 /* Handle backslash */
113 if (*t == '\\') t++;
115 /* Require a character */
116 if (!*t) break;
118 /* Advance */
119 t++;
121 /* Hack -- Require a close quote */
122 if (*t != '\'') *t = '\'';
125 /* Handle back-slash */
126 if (*t == '\\') t++;
129 /* Nothing left */
130 if (!*t) break;
132 /* Nuke and advance */
133 *t++ = '\0';
135 /* Save the token */
136 tokens[i++] = s;
138 /* Advance */
139 s = t;
142 /* Save the token */
143 tokens[i++] = s;
145 /* Number found */
146 return (i);
152 * Parse a sub-file of the "extra info" (format shown below)
154 * Each "action" line has an "action symbol" in the first column,
155 * followed by a colon, followed by some command specific info,
156 * usually in the form of "tokens" separated by colons or slashes.
158 * Blank lines, lines starting with white space, and lines starting
159 * with pound signs ("#") are ignored (as comments).
161 * Note the use of "tokenize()" to allow the use of both colons and
162 * slashes as delimeters, while still allowing final tokens which
163 * may contain any characters including "delimiters".
165 * Note the use of "strtol()" to allow all "integers" to be encoded
166 * in decimal, hexidecimal, or octal form.
168 * Note that "monster zero" is used for the "player" attr/char, "object
169 * zero" will be used for the "stack" attr/char, and "feature zero" is
170 * used for the "nothing" attr/char.
172 * Specify the attr/char values for "monsters" by race index.
173 * R:<num>:<a>/<c>
175 * Specify the attr/char values for "objects" by kind index.
176 * K:<num>:<a>/<c>
178 * Specify the attr/char values for "features" by feature index.
179 * F:<num>:<a>/<c>
181 * Specify the attr/char values for "special" things.
182 * S:<num>:<a>/<c>
184 * Specify the attribute values for inventory "objects" by kind tval.
185 * E:<tv>:<a>
187 * Define a macro action, given an encoded macro action.
188 * A:<str>
190 * Create a macro, given an encoded macro trigger.
191 * P:<str>
193 * Create a keymap, given an encoded keymap trigger.
194 * C:<num>:<str>
196 * Turn an option off, given its name.
197 * X:<str>
199 * Turn an option on, given its name.
200 * Y:<str>
202 * Turn a window flag on or off, given a window, flag, and value.
203 * W:<win>:<flag>:<value>
205 * Specify visual information, given an index, and some data.
206 * V:<num>:<kv>:<rv>:<gv>:<bv>
208 * Specify colors for message-types.
209 * M:<type>:<attr>
211 * Specify the attr/char values for "flavors" by flavors index.
212 * L:<num>:<a>/<c>
214 errr process_pref_file_command(char *buf)
216 long i, n1, n2, sq;
218 char *zz[16];
221 /* Skip "empty" lines */
222 if (!buf[0]) return (0);
224 /* Skip "blank" lines */
225 if (isspace((unsigned char)buf[0])) return (0);
227 /* Skip comments */
228 if (buf[0] == '#') return (0);
231 /* Paranoia */
232 /* if (strlen(buf) >= 1024) return (1); */
235 /* Require "?:*" format */
236 if (buf[1] != ':') return (1);
239 /* Process "R:<num>:<a>/<c>" -- attr/char for monster races */
240 if (buf[0] == 'R')
242 if (tokenize(buf+2, 3, zz) == 3)
244 monster_race *r_ptr;
245 i = strtol(zz[0], NULL, 0);
246 n1 = strtol(zz[1], NULL, 0);
247 n2 = strtol(zz[2], NULL, 0);
248 if ((i < 0) || (i >= (long)z_info->r_max)) return (1);
249 r_ptr = &r_info[i];
250 if (n1) r_ptr->x_attr = (byte)n1;
251 if (n2) r_ptr->x_char = (char)n2;
252 return (0);
256 /* Process "B:<k_idx>:inscription */
257 else if (buf[0] == 'B')
259 if (2 == tokenize(buf + 2, 2, zz))
261 add_autoinscription(strtol(zz[0], NULL, 0), zz[1]);
262 return (0);
266 /* Process "Q:<idx>:<tval>:<sval>:<y|n>" -- squelch bits */
267 /* and "Q:<idx>:<val>" -- squelch levels */
268 /* and "Q:<val>" -- auto_destroy */
269 else if (buf[0] == 'Q')
271 i = tokenize(buf+2, 4, zz);
272 if (i == 2)
274 n1 = strtol(zz[0], NULL, 0);
275 n2 = strtol(zz[1], NULL, 0);
276 squelch_level[n1] = n2;
277 return(0);
279 else if (i == 4)
281 i = strtol(zz[0], NULL, 0);
282 n1 = strtol(zz[1], NULL, 0);
283 n2 = strtol(zz[2], NULL, 0);
284 sq = strtol(zz[3], NULL, 0);
285 if ((k_info[i].tval == n1) && (k_info[i].sval == n2))
287 k_info[i].squelch = sq;
288 return(0);
290 else
292 for (i = 1; i < z_info->k_max; i++)
294 if ((k_info[i].tval == n1) && (k_info[i].sval == n2))
296 k_info[i].squelch = sq;
297 return(0);
304 /* Process "K:<num>:<a>/<c>" -- attr/char for object kinds */
305 else if (buf[0] == 'K')
307 if (tokenize(buf+2, 3, zz) == 3)
309 object_kind *k_ptr;
310 i = strtol(zz[0], NULL, 0);
311 n1 = strtol(zz[1], NULL, 0);
312 n2 = strtol(zz[2], NULL, 0);
313 if ((i < 0) || (i >= (long)z_info->k_max)) return (1);
314 k_ptr = &k_info[i];
315 if (n1) k_ptr->x_attr = (byte)n1;
316 if (n2) k_ptr->x_char = (char)n2;
317 return (0);
322 /* Process "F:<num>:<a>/<c>" -- attr/char for terrain features */
323 else if (buf[0] == 'F')
325 if (tokenize(buf+2, 3, zz) == 3)
327 feature_type *f_ptr;
328 i = strtol(zz[0], NULL, 0);
329 n1 = strtol(zz[1], NULL, 0);
330 n2 = strtol(zz[2], NULL, 0);
331 if ((i < 0) || (i >= (long)z_info->f_max)) return (1);
332 f_ptr = &f_info[i];
333 if (n1) f_ptr->x_attr = (byte)n1;
334 if (n2) f_ptr->x_char = (char)n2;
335 return (0);
340 /* Process "L:<num>:<a>/<c>" -- attr/char for flavors */
341 else if (buf[0] == 'L')
343 if (tokenize(buf+2, 3, zz) == 3)
345 flavor_type *flavor_ptr;
346 i = strtol(zz[0], NULL, 0);
347 n1 = strtol(zz[1], NULL, 0);
348 n2 = strtol(zz[2], NULL, 0);
349 if ((i < 0) || (i >= (long)z_info->flavor_max)) return (1);
350 flavor_ptr = &flavor_info[i];
351 if (n1) flavor_ptr->x_attr = (byte)n1;
352 if (n2) flavor_ptr->x_char = (char)n2;
353 return (0);
358 /* Process "S:<num>:<a>/<c>" -- attr/char for special things */
359 else if (buf[0] == 'S')
361 if (tokenize(buf+2, 3, zz) == 3)
363 i = strtol(zz[0], NULL, 0);
364 n1 = strtol(zz[1], NULL, 0);
365 n2 = strtol(zz[2], NULL, 0);
366 if ((i < 0) || (i >= (long)N_ELEMENTS(misc_to_attr))) return (1);
367 misc_to_attr[i] = (byte)n1;
368 misc_to_char[i] = (char)n2;
369 return (0);
374 /* Process "E:<tv>:<a>" -- attribute for inventory objects */
375 else if (buf[0] == 'E')
377 if (tokenize(buf+2, 2, zz) == 2)
379 i = strtol(zz[0], NULL, 0) % 128;
380 n1 = strtol(zz[1], NULL, 0);
381 if ((i < 0) || (i >= (long)N_ELEMENTS(tval_to_attr))) return (1);
382 if (n1) tval_to_attr[i] = (byte)n1;
383 return (0);
388 /* Process "A:<str>" -- save an "action" for later */
389 else if (buf[0] == 'A')
391 text_to_ascii(macro_buffer, sizeof(macro_buffer), buf+2);
392 return (0);
395 /* Process "P:<str>" -- create macro */
396 else if (buf[0] == 'P')
398 char tmp[1024];
399 text_to_ascii(tmp, sizeof(tmp), buf+2);
400 macro_add(tmp, macro_buffer);
401 return (0);
404 /* Process "C:<num>:<str>" -- create keymap */
405 else if (buf[0] == 'C')
407 long mode;
409 char tmp[1024];
411 if (tokenize(buf+2, 2, zz) != 2) return (1);
413 mode = strtol(zz[0], NULL, 0);
414 if ((mode < 0) || (mode >= KEYMAP_MODES)) return (1);
416 text_to_ascii(tmp, sizeof(tmp), zz[1]);
417 if (!tmp[0] || tmp[1]) return (1);
418 i = (long)tmp[0];
420 string_free(keymap_act[mode][i]);
422 keymap_act[mode][i] = string_make(macro_buffer);
424 return (0);
428 /* Process "V:<num>:<kv>:<rv>:<gv>:<bv>" -- visual info */
429 else if (buf[0] == 'V')
431 if (tokenize(buf+2, 5, zz) == 5)
433 i = strtol(zz[0], NULL, 0);
434 if ((i < 0) || (i >= MAX_COLORS)) return (1);
435 angband_color_table[i][0] = (byte)strtol(zz[1], NULL, 0);
436 angband_color_table[i][1] = (byte)strtol(zz[2], NULL, 0);
437 angband_color_table[i][2] = (byte)strtol(zz[3], NULL, 0);
438 angband_color_table[i][3] = (byte)strtol(zz[4], NULL, 0);
439 return (0);
443 /* set macro trigger names and a template */
444 /* Process "T:<trigger>:<keycode>:<shift-keycode>" */
445 /* Process "T:<template>:<modifier chr>:<modifier name>:..." */
446 else if (buf[0] == 'T')
448 int tok;
450 tok = tokenize(buf + 2, MAX_MACRO_MOD + 2, zz);
452 /* Trigger template */
453 if (tok >= 4)
455 int i;
456 int num;
458 /* Free existing macro triggers and trigger template */
459 macro_trigger_free();
461 /* Clear template done */
462 if (*zz[0] == '\0') return 0;
464 /* Count modifier-characters */
465 num = strlen(zz[1]);
467 /* One modifier-character per modifier */
468 if (num + 2 != tok) return 1;
470 /* Macro template */
471 macro_template = string_make(zz[0]);
473 /* Modifier chars */
474 macro_modifier_chr = string_make(zz[1]);
476 /* Modifier names */
477 for (i = 0; i < num; i++)
479 macro_modifier_name[i] = string_make(zz[2+i]);
482 /* Macro trigger */
483 else if (tok >= 2)
485 char *buf;
486 cptr s;
487 char *t;
489 if (max_macrotrigger >= MAX_MACRO_TRIGGER)
491 msg_print("Too many macro triggers!");
492 return 1;
495 /* Buffer for the trigger name */
496 C_MAKE(buf, strlen(zz[0]) + 1, char);
498 /* Simulate strcpy() and skip the '\' escape character */
499 s = zz[0];
500 t = buf;
502 while (*s)
504 if ('\\' == *s) s++;
505 *t++ = *s++;
508 /* Terminate the trigger name */
509 *t = '\0';
511 /* Store the trigger name */
512 macro_trigger_name[max_macrotrigger] = string_make(buf);
514 /* Free the buffer */
515 FREE(buf);
517 /* Normal keycode */
518 macro_trigger_keycode[0][max_macrotrigger] = string_make(zz[1]);
520 /* Special shifted keycode */
521 if (tok == 3)
523 macro_trigger_keycode[1][max_macrotrigger] = string_make(zz[2]);
525 /* Shifted keycode is the same as the normal keycode */
526 else
528 macro_trigger_keycode[1][max_macrotrigger] = string_make(zz[1]);
531 /* Count triggers */
532 max_macrotrigger++;
535 return 0;
538 /* Process "X:<str>" -- turn option off */
539 else if (buf[0] == 'X')
541 /* Check non-adult options */
542 for (i = 0; i < OPT_ADULT; i++)
544 if (option_text[i] && streq(option_text[i], buf + 2))
546 op_ptr->opt[i] = FALSE;
547 return (0);
551 /* Ignore unknown options */
552 return (0);
555 /* Process "Y:<str>" -- turn option on */
556 else if (buf[0] == 'Y')
558 /* Check non-adult options */
559 for (i = 0; i < OPT_ADULT; i++)
561 if (option_text[i] && streq(option_text[i], buf + 2))
563 op_ptr->opt[i] = TRUE;
564 return (0);
568 /* Ignore unknown options */
569 return (0);
573 /* Process "W:<win>:<flag>:<value>" -- window flags */
574 else if (buf[0] == 'W')
576 long win, flag, value;
578 if (tokenize(buf + 2, 3, zz) == 3)
580 win = strtol(zz[0], NULL, 0);
581 flag = strtol(zz[1], NULL, 0);
582 value = strtol(zz[2], NULL, 0);
584 /* Ignore illegal windows */
585 /* Hack -- Ignore the main window */
586 if ((win <= 0) || (win >= ANGBAND_TERM_MAX)) return (1);
588 /* Ignore illegal flags */
589 if ((flag < 0) || (flag >= (int)N_ELEMENTS(window_flag_desc))) return (1);
591 /* Require a real flag */
592 if (window_flag_desc[flag])
594 if (value)
596 /* Turn flag on */
597 op_ptr->window_flag[win] |= (1L << flag);
599 else
601 /* Turn flag off */
602 op_ptr->window_flag[win] &= ~(1L << flag);
606 /* Success */
607 return (0);
612 /* Process "M:<type>:<attr>" -- colors for message-types */
613 else if (buf[0] == 'M')
615 if (tokenize(buf+2, 2, zz) == 2)
617 long type = strtol(zz[0], NULL, 0);
618 int color = color_char_to_attr(zz[1][0]);
620 /* Ignore illegal color */
621 if (color < 0) return (1);
623 /* Store the color */
624 return (message_color_define((u16b)type, (byte)color));
629 /* Failure */
630 return (1);
635 * Helper function for "process_pref_file()"
637 * Input:
638 * v: output buffer array
639 * f: final character
641 * Output:
642 * result
644 static cptr process_pref_file_expr(char **sp, char *fp)
646 cptr v;
648 char *b;
649 char *s;
651 char b1 = '[';
652 char b2 = ']';
654 char f = ' ';
656 /* Initial */
657 s = (*sp);
659 /* Skip spaces */
660 while (isspace((unsigned char)*s)) s++;
662 /* Save start */
663 b = s;
665 /* Default */
666 v = "?o?o?";
668 /* Analyze */
669 if (*s == b1)
671 const char *p;
672 const char *t;
674 /* Skip b1 */
675 s++;
677 /* First */
678 t = process_pref_file_expr(&s, &f);
680 /* Oops */
681 if (!*t)
683 /* Nothing */
686 /* Function: IOR */
687 else if (streq(t, "IOR"))
689 v = "0";
690 while (*s && (f != b2))
692 t = process_pref_file_expr(&s, &f);
693 if (*t && !streq(t, "0")) v = "1";
697 /* Function: AND */
698 else if (streq(t, "AND"))
700 v = "1";
701 while (*s && (f != b2))
703 t = process_pref_file_expr(&s, &f);
704 if (*t && streq(t, "0")) v = "0";
708 /* Function: NOT */
709 else if (streq(t, "NOT"))
711 v = "1";
712 while (*s && (f != b2))
714 t = process_pref_file_expr(&s, &f);
715 if (*t && !streq(t, "0")) v = "0";
719 /* Function: EQU */
720 else if (streq(t, "EQU"))
722 v = "1";
723 if (*s && (f != b2))
725 t = process_pref_file_expr(&s, &f);
727 while (*s && (f != b2))
729 p = t;
730 t = process_pref_file_expr(&s, &f);
731 if (*t && !streq(p, t)) v = "0";
735 /* Function: LEQ */
736 else if (streq(t, "LEQ"))
738 v = "1";
739 if (*s && (f != b2))
741 t = process_pref_file_expr(&s, &f);
743 while (*s && (f != b2))
745 p = t;
746 t = process_pref_file_expr(&s, &f);
747 if (*t && (strcmp(p, t) >= 0)) v = "0";
751 /* Function: GEQ */
752 else if (streq(t, "GEQ"))
754 v = "1";
755 if (*s && (f != b2))
757 t = process_pref_file_expr(&s, &f);
759 while (*s && (f != b2))
761 p = t;
762 t = process_pref_file_expr(&s, &f);
763 if (*t && (strcmp(p, t) <= 0)) v = "0";
767 /* Oops */
768 else
770 while (*s && (f != b2))
772 t = process_pref_file_expr(&s, &f);
776 /* Verify ending */
777 if (f != b2) v = "?x?x?";
779 /* Extract final and Terminate */
780 if ((f = *s) != '\0') *s++ = '\0';
783 /* Other */
784 else
786 /* Accept all printables except spaces and brackets */
787 while (isprint((unsigned char)*s) && !strchr(" []", *s)) ++s;
789 /* Extract final and Terminate */
790 if ((f = *s) != '\0') *s++ = '\0';
792 /* Variable */
793 if (*b == '$')
795 /* System */
796 if (streq(b+1, "SYS"))
798 v = ANGBAND_SYS;
801 /* Graphics */
802 else if (streq(b+1, "GRAF"))
804 v = ANGBAND_GRAF;
807 /* Race */
808 else if (streq(b+1, "RACE"))
810 v = p_name + rp_ptr->name;
813 /* Class */
814 else if (streq(b+1, "CLASS"))
816 v = c_name + cp_ptr->name;
819 /* Player */
820 else if (streq(b+1, "PLAYER"))
822 v = op_ptr->base_name;
825 /* Game version */
826 else if (streq(b+1, "VERSION"))
828 v = VERSION_STRING;
832 /* Constant */
833 else
835 v = b;
839 /* Save */
840 (*fp) = f;
842 /* Save */
843 (*sp) = s;
845 /* Result */
846 return (v);
851 * Open the "user pref file" and parse it.
853 static errr process_pref_file_aux(cptr name)
855 FILE *fp;
857 char buf[1024];
859 char old[1024];
861 int line = -1;
863 errr err = 0;
865 bool bypass = FALSE;
868 /* Open the file */
869 fp = my_fopen(name, "r");
871 /* No such file */
872 if (!fp) return (-1);
875 /* Process the file */
876 while (0 == my_fgets(fp, buf, sizeof(buf)))
878 /* Count lines */
879 line++;
882 /* Skip "empty" lines */
883 if (!buf[0]) continue;
885 /* Skip "blank" lines */
886 if (isspace((unsigned char)buf[0])) continue;
888 /* Skip comments */
889 if (buf[0] == '#') continue;
892 /* Save a copy */
893 my_strcpy(old, buf, sizeof(old));
896 /* Process "?:<expr>" */
897 if ((buf[0] == '?') && (buf[1] == ':'))
899 char f;
900 cptr v;
901 char *s;
903 /* Start */
904 s = buf + 2;
906 /* Parse the expr */
907 v = process_pref_file_expr(&s, &f);
909 /* Set flag */
910 bypass = (streq(v, "0") ? TRUE : FALSE);
912 /* Continue */
913 continue;
916 /* Apply conditionals */
917 if (bypass) continue;
920 /* Process "%:<file>" */
921 if (buf[0] == '%')
923 /* Process that file if allowed */
924 (void)process_pref_file(buf + 2);
926 /* Continue */
927 continue;
931 /* Process the line */
932 err = process_pref_file_command(buf);
934 /* Oops */
935 if (err) break;
939 /* Error */
940 if (err)
942 /* Print error message */
943 /* ToDo: Add better error messages */
944 msg_format("Error %d in line %d of file '%s'.", err, line, name);
945 msg_format("Parsing '%s'", old);
946 message_flush();
949 /* Close the file */
950 my_fclose(fp);
952 /* Result */
953 return (err);
959 * Process the "user pref file" with the given name
961 * See the functions above for a list of legal "commands".
963 * We also accept the special "?" and "%" directives, which
964 * allow conditional evaluation and filename inclusion.
966 errr process_pref_file(cptr name)
968 char buf[1024];
970 errr err = 0;
973 /* Build the filename */
974 path_build(buf, sizeof(buf), ANGBAND_DIR_PREF, name);
976 /* Process the pref file */
977 err = process_pref_file_aux(buf);
979 /* Stop at parser errors, but not at non-existing file */
980 if (err < 1)
982 /* Build the filename */
983 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
985 /* Process the pref file */
986 err = process_pref_file_aux(buf);
989 /* Result */
990 return (err);
995 * Returns a "rating" of x depending on y, and sets "attr" to the
996 * corresponding "attribute".
998 static cptr likert(int x, int y, byte *attr)
1000 /* Paranoia */
1001 if (y <= 0) y = 1;
1003 /* Negative value */
1004 if (x < 0)
1006 *attr = TERM_RED;
1007 return ("Very Bad");
1010 /* Analyze the value */
1011 switch ((x / y))
1013 case 0:
1014 case 1:
1016 *attr = TERM_RED;
1017 return ("Bad");
1019 case 2:
1021 *attr = TERM_RED;
1022 return ("Poor");
1024 case 3:
1025 case 4:
1027 *attr = TERM_YELLOW;
1028 return ("Fair");
1030 case 5:
1032 *attr = TERM_YELLOW;
1033 return ("Good");
1035 case 6:
1037 *attr = TERM_YELLOW;
1038 return ("Very Good");
1040 case 7:
1041 case 8:
1043 *attr = TERM_L_GREEN;
1044 return ("Excellent");
1046 case 9:
1047 case 10:
1048 case 11:
1049 case 12:
1050 case 13:
1052 *attr = TERM_L_GREEN;
1053 return ("Superb");
1055 case 14:
1056 case 15:
1057 case 16:
1058 case 17:
1060 *attr = TERM_L_GREEN;
1061 return ("Heroic");
1063 default:
1065 *attr = TERM_L_GREEN;
1066 return ("Legendary");
1073 * Obtain the "flags" for the player as if he was an item
1075 void player_flags(u32b *f1, u32b *f2, u32b *f3)
1077 /* Clear */
1078 (*f1) = (*f2) = (*f3) = 0L;
1080 /* Add racial flags */
1081 (*f1) |= rp_ptr->flags1;
1082 (*f2) |= rp_ptr->flags2;
1083 (*f3) |= rp_ptr->flags3;
1085 if (cp_ptr->flags & CF_BRAVERY_30)
1087 if (p_ptr->lev >= 30) (*f2) |= (TR2_RES_FEAR);
1093 * Equippy chars
1095 static void display_player_equippy(int y, int x)
1097 int i;
1099 byte a;
1100 char c;
1102 object_type *o_ptr;
1105 /* Dump equippy chars */
1106 for (i = INVEN_WIELD; i < INVEN_TOTAL; ++i)
1108 /* Object */
1109 o_ptr = &inventory[i];
1111 /* Skip empty objects */
1112 if (!o_ptr->k_idx) continue;
1114 /* Get attr/char for display */
1115 a = object_attr(o_ptr);
1116 c = object_char(o_ptr);
1118 /* Dump */
1119 Term_putch(x+i-INVEN_WIELD, y, a, c);
1124 * 'Database' of resistances and abilities to display
1126 #define RES_ROWS 8
1127 struct player_flag_record {
1128 const char name[7]; /* Name of resistance/ability */
1129 byte set; /* Which field this resistance is in { 1 2 3 } */
1130 u32b res_flag; /* resistance flag bit */
1131 u32b im_flag; /* corresponding immunity bit, if any. */
1133 static const struct player_flag_record player_flag_table[RES_ROWS*4] =
1135 { " Acid", 2, TR2_RES_ACID, TR2_IM_ACID },
1136 { " Elec", 2, TR2_RES_ELEC, TR2_IM_ELEC },
1137 { " Fire", 2, TR2_RES_FIRE, TR2_IM_FIRE },
1138 { " Cold", 2, TR2_RES_COLD, TR2_IM_COLD },
1139 { " Pois", 2, TR2_RES_POIS, 0 }, /* TR2_IM_POIS */
1140 { " Fear", 2, TR2_RES_FEAR, 0 },
1141 { " Lite", 2, TR2_RES_LITE, 0 },
1142 { " Dark", 2, TR2_RES_DARK, 0 },
1144 { "Blind", 2, TR2_RES_BLIND, 0 },
1145 { "Confu", 2, TR2_RES_CONFU, 0 },
1146 { "Sound", 2, TR2_RES_SOUND, 0 },
1147 { "Shard", 2, TR2_RES_SHARD, 0 },
1148 { "Nexus", 2, TR2_RES_NEXUS, 0 },
1149 { "Nethr", 2, TR2_RES_NETHR, 0 },
1150 { "Chaos", 2, TR2_RES_CHAOS, 0 },
1151 { "Disen", 2, TR2_RES_DISEN, 0 },
1153 { "S.Dig", 3, TR3_SLOW_DIGEST, 0 },
1154 { "Feath", 3, TR3_FEATHER, 0 },
1155 { "PLite", 3, TR3_LITE, 0 },
1156 { "Regen", 3, TR3_REGEN, 0 },
1157 { "Telep", 3, TR3_TELEPATHY, 0 },
1158 { "Invis", 3, TR3_SEE_INVIS, 0 },
1159 { "FrAct", 3, TR3_FREE_ACT, 0 },
1160 { "HLife", 3, TR3_HOLD_LIFE, 0 },
1162 { "Stea.", 1, TR1_STEALTH, 0 },
1163 { "Sear.", 1, TR1_SEARCH, 0 },
1164 { "Infra", 1, TR1_INFRA, 0 },
1165 { "Tunn.", 1, TR1_TUNNEL, 0 },
1166 { "Speed", 1, TR1_SPEED, 0 },
1167 { "Blows", 1, TR1_BLOWS, 0 },
1168 { "Shots", 1, TR1_SHOTS, 0 },
1169 { "Might", 1, TR1_MIGHT, 0 },
1172 #define RES_COLS (5 + 2 + INVEN_TOTAL - INVEN_WIELD)
1173 static const region resist_region[] =
1175 { 0*(RES_COLS+1), 11, RES_COLS, RES_ROWS+2 },
1176 { 1*(RES_COLS+1), 11, RES_COLS, RES_ROWS+2 },
1177 { 2*(RES_COLS+1), 11, RES_COLS, RES_ROWS+2 },
1178 { 3*(RES_COLS+1), 11, RES_COLS, RES_ROWS+2 },
1181 static void display_resistance_panel(const struct player_flag_record *resists,
1182 size_t size, const region *bounds)
1184 size_t i, j;
1185 int col = bounds->col;
1186 int row = bounds->row;
1187 Term_putstr(col, row++, RES_COLS, TERM_WHITE, " abcdefghijkl@");
1188 for (i = 0; i < size-3; i++, row++)
1190 byte name_attr = TERM_WHITE;
1191 Term_gotoxy(col+6, row);
1192 /* repeated extraction of flags is inefficient but more natural */
1193 for (j = INVEN_WIELD; j <= INVEN_TOTAL; j++)
1195 object_type *o_ptr = &inventory[j];
1196 byte attr = TERM_WHITE | (j % 2) * 8; /* alternating columns */
1197 u32b f[4] = {0, 0, 0, 0};
1198 bool res, imm;
1199 char sym;
1200 if(j < INVEN_TOTAL)
1201 object_flags_known(o_ptr, &f[1], &f[2], &f[3]);
1202 else
1203 player_flags(&f[1], &f[2], &f[3]);
1205 res = (0 != (f[resists[i].set] & resists[i].res_flag));
1206 imm = (0 != (f[resists[i].set] & resists[i].im_flag));
1207 if(imm) name_attr = TERM_GREEN;
1208 else if(res && name_attr == TERM_WHITE) name_attr = TERM_L_BLUE;
1209 sym = imm ? '*' : ( res ? '+' : '.' );
1210 Term_addch(attr, sym);
1212 Term_putstr(col, row, 6, name_attr, format("%5s:", resists[i].name));
1214 Term_putstr(col, row++, RES_COLS, TERM_WHITE, " abcdefghijkl@");
1215 /* Equippy */
1216 display_player_equippy(row++, col+6);
1219 static void display_player_flag_info(void)
1221 int i;
1222 for (i = 0; i < 4; i++)
1224 display_resistance_panel(player_flag_table+(i*RES_ROWS), RES_ROWS+3,
1225 &resist_region[i]);
1231 * Special display, part 2b
1233 static void display_player_stat_info(void)
1235 int i, row, col;
1237 char buf[80];
1240 /* Row */
1241 row = 3;
1243 /* Column */
1244 col = 42;
1246 /* Print out the labels for the columns */
1247 c_put_str(TERM_WHITE, " Self", row-1, col+5);
1248 c_put_str(TERM_WHITE, " RB", row-1, col+12);
1249 c_put_str(TERM_WHITE, " CB", row-1, col+16);
1250 c_put_str(TERM_WHITE, " EB", row-1, col+20);
1251 c_put_str(TERM_WHITE, " Best", row-1, col+24);
1253 /* Display the stats */
1254 for (i = 0; i < A_MAX; i++)
1256 /* Reduced */
1257 if (p_ptr->stat_use[i] < p_ptr->stat_top[i])
1259 /* Use lowercase stat name */
1260 put_str(stat_names_reduced[i], row+i, col);
1263 /* Normal */
1264 else
1266 /* Assume uppercase stat name */
1267 put_str(stat_names[i], row+i, col);
1270 /* Indicate natural maximum */
1271 if (p_ptr->stat_max[i] == 18+100)
1273 put_str("!", row+i, col+3);
1276 /* Internal "natural" maximum value */
1277 cnv_stat(p_ptr->stat_max[i], buf, sizeof(buf));
1278 c_put_str(TERM_L_GREEN, buf, row+i, col+5);
1280 /* Race Bonus */
1281 strnfmt(buf, sizeof(buf), "%+3d", rp_ptr->r_adj[i]);
1282 c_put_str(TERM_L_BLUE, buf, row+i, col+12);
1284 /* Class Bonus */
1285 strnfmt(buf, sizeof(buf), "%+3d", cp_ptr->c_adj[i]);
1286 c_put_str(TERM_L_BLUE, buf, row+i, col+16);
1288 /* Equipment Bonus */
1289 strnfmt(buf, sizeof(buf), "%+3d", p_ptr->stat_add[i]);
1290 c_put_str(TERM_L_BLUE, buf, row+i, col+20);
1292 /* Resulting "modified" maximum value */
1293 cnv_stat(p_ptr->stat_top[i], buf, sizeof(buf));
1294 c_put_str(TERM_L_GREEN, buf, row+i, col+24);
1296 /* Only display stat_use if not maximal */
1297 if (p_ptr->stat_use[i] < p_ptr->stat_top[i])
1299 cnv_stat(p_ptr->stat_use[i], buf, sizeof(buf));
1300 c_put_str(TERM_YELLOW, buf, row+i, col+31);
1307 * Special display, part 2c
1309 * How to print out the modifications and sustains.
1310 * Positive mods with no sustain will be light green.
1311 * Positive mods with a sustain will be dark green.
1312 * Sustains (with no modification) will be a dark green 's'.
1313 * Negative mods (from a curse) will be red.
1314 * Huge mods (>9), like from MICoMorgoth, will be a '*'
1315 * No mod, no sustain, will be a slate '.'
1317 static void display_player_sust_info(void)
1319 int i, row, col, stat;
1321 object_type *o_ptr;
1322 u32b f1, f2, f3;
1323 u32b ignore_f2, ignore_f3;
1325 byte a;
1326 char c;
1329 /* Row */
1330 row = 3;
1332 /* Column */
1333 col = 26;
1335 /* Header */
1336 c_put_str(TERM_WHITE, "abcdefghijkl@", row-1, col);
1338 /* Process equipment */
1339 for (i = INVEN_WIELD; i < INVEN_TOTAL; ++i)
1341 /* Get the object */
1342 o_ptr = &inventory[i];
1344 /* Get the "known" flags */
1345 object_flags_known(o_ptr, &f1, &f2, &f3);
1347 /* Hack -- assume stat modifiers are known */
1348 object_flags(o_ptr, &f1, &ignore_f2, &ignore_f3);
1350 /* Initialize color based of sign of pval. */
1351 for (stat = 0; stat < A_MAX; stat++)
1353 /* Default */
1354 a = TERM_SLATE;
1355 c = '.';
1357 /* Boost */
1358 if (f1 & (1<<stat))
1360 /* Default */
1361 c = '*';
1363 /* Good */
1364 if (o_ptr->pval > 0)
1366 /* Good */
1367 a = TERM_L_GREEN;
1369 /* Label boost */
1370 if (o_ptr->pval < 10) c = I2D(o_ptr->pval);
1373 /* Bad */
1374 if (o_ptr->pval < 0)
1376 /* Bad */
1377 a = TERM_RED;
1379 /* Label boost */
1380 if (o_ptr->pval > -10) c = I2D(-(o_ptr->pval));
1384 /* Sustain */
1385 if (f2 & (1<<stat))
1387 /* Dark green */
1388 a = TERM_GREEN;
1390 /* Convert '.' to 's' */
1391 if (c == '.') c = 's';
1394 /* Dump proper character */
1395 Term_putch(col, row+stat, a, c);
1398 /* Advance */
1399 col++;
1402 /* Player flags */
1403 player_flags(&f1, &f2, &f3);
1405 /* Check stats */
1406 for (stat = 0; stat < A_MAX; ++stat)
1408 /* Default */
1409 a = TERM_SLATE;
1410 c = '.';
1412 /* Sustain */
1413 if (f2 & (1<<stat))
1415 /* Dark green "s" */
1416 a = TERM_GREEN;
1417 c = 's';
1420 /* Dump */
1421 Term_putch(col, row+stat, a, c);
1424 /* Column */
1425 col = 26;
1427 /* Footer */
1428 c_put_str(TERM_WHITE, "abcdefghijkl@", row+6, col);
1430 /* Equippy */
1431 display_player_equippy(row+7, col);
1435 static const region boundaries [] =
1437 { 0, 0, 0, 0 },
1438 { 1, 2, 40, 8 }, /* Name, Class, ... */
1439 { 1, 10, 18, 8 }, /* Cur Exp, Max Exp, ... */
1440 { 26, 10, 17, 8 }, /* AC, melee, ... */
1441 { 48, 10, 24, 8 }, /* skills */
1442 { 26, 3, 13, 5 }, /* Age, ht, wt, ... */
1447 static const char *show_title(void)
1449 if (p_ptr->wizard)
1450 return "[=-WIZARD-=]";
1451 else if (p_ptr->total_winner || p_ptr->lev > PY_MAX_LEVEL)
1452 return "***WINNER***";
1453 else
1454 return c_text + cp_ptr->title[(p_ptr->lev - 1) / 5];
1457 static const char *show_adv_exp(void)
1459 if (p_ptr->lev < PY_MAX_LEVEL)
1461 static char buffer[30];
1462 s32b advance = (player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L);
1463 strnfmt(buffer, sizeof(buffer), "%d", advance);
1464 return buffer;
1466 else {
1467 return "********";
1471 static const char *show_depth(void)
1473 static char buffer[10];
1474 if (p_ptr->max_depth == 0) return "Town";
1475 else if (depth_in_feet)
1477 strnfmt(buffer, sizeof(buffer), "%d ft", p_ptr->max_depth * 50);
1478 return buffer;
1480 else {
1481 strnfmt(buffer, sizeof(buffer), "Lev %d", p_ptr->max_depth);
1482 return buffer;
1486 static const char *show_speed()
1488 static char buffer[10];
1489 int tmp = p_ptr->pspeed;
1490 if (p_ptr->timed[TMD_FAST]) tmp -= 10;
1491 if (p_ptr->timed[TMD_SLOW]) tmp += 10;
1492 if (p_ptr->searching) tmp += 10;
1493 if (tmp == 110) return "Normal";
1494 strnfmt(buffer, sizeof(buffer), "%d", tmp - 110);
1495 return buffer;
1498 static const char *show_melee_weapon(const object_type *o_ptr)
1500 static char buffer[12];
1501 int hit = p_ptr->dis_to_h;
1502 int dam = p_ptr->dis_to_d;
1504 if (object_known_p(o_ptr))
1506 hit += o_ptr->to_h;
1507 dam += o_ptr->to_d;
1510 strnfmt(buffer, sizeof(buffer), "(%+d,%+d)", hit, dam);
1511 return buffer;
1514 static const char *show_missile_weapon(const object_type *o_ptr)
1516 static char buffer[12];
1517 int hit = p_ptr->dis_to_h;
1518 int dam = 0;
1520 if (object_known_p(o_ptr))
1522 hit += o_ptr->to_h;
1523 dam += o_ptr->to_d;
1526 strnfmt(buffer, sizeof(buffer), "(%+d,%+d)", hit, dam);
1527 return buffer;
1530 static byte max_color(int val, int max)
1532 return val < max ? TERM_YELLOW : TERM_L_GREEN;
1535 /* data_panel array element initializer, for ansi compliance */
1536 #define P_I(col, lab, format, val1, val2) \
1537 { panel[i].color = col; panel[i].label = lab; panel[i].fmt = format; \
1538 panel[i].value[0] = val1; panel[i].value[1] = val2; \
1539 i++; }
1541 int get_panel(int oid, data_panel *panel, size_t size)
1543 int ret = (s32b) size;
1544 switch(oid)
1546 case 1:
1548 int i = 0;
1549 assert( size >= (u32b) boundaries[1].page_rows);
1550 ret = boundaries[1].page_rows;
1551 P_I(TERM_L_BLUE, "Name", "%y", s2u(op_ptr->full_name), END );
1552 P_I(TERM_L_BLUE, "Sex", "%y", s2u(sp_ptr->title), END );
1553 P_I(TERM_L_BLUE, "Race", "%y", s2u(p_name + rp_ptr->name), END );
1554 P_I(TERM_L_BLUE, "Class", "%y", s2u(c_name + cp_ptr->name), END );
1555 P_I(TERM_L_BLUE, "Title", "%y", s2u(show_title()), END );
1556 P_I(TERM_L_BLUE, "HP", "%y/%y", i2u(p_ptr->chp), i2u(p_ptr->mhp) );
1557 P_I(TERM_L_BLUE, "SP", "%y/%y", i2u(p_ptr->csp), i2u(p_ptr->msp) );
1558 P_I(TERM_L_BLUE, "Level", "%y", i2u(p_ptr->lev), END );
1559 assert(i == boundaries[1].page_rows);
1560 return ret;
1562 case 2:
1564 int i = 0;
1565 assert( ret >= boundaries[2].page_rows);
1566 ret = boundaries[2].page_rows;
1567 P_I(max_color(p_ptr->lev, p_ptr->max_lev), "Level", "%y", i2u(p_ptr->lev), END );
1568 P_I(max_color(p_ptr->exp, p_ptr->max_exp), "Cur Exp", "%y", i2u(p_ptr->exp), END );
1569 P_I(TERM_L_GREEN, "Max Exp", "%y", i2u(p_ptr->max_exp), END );
1570 P_I(TERM_L_GREEN, "Adv Exp", "%y", s2u(show_adv_exp()), END );
1571 P_I(TERM_L_GREEN, "MaxDepth", "%y", s2u(show_depth()), END );
1572 P_I(TERM_L_GREEN, "Turns", "%y", i2u(turn), END );
1573 P_I(TERM_L_GREEN, "Gold", "%y", i2u(p_ptr->au), END );
1574 P_I(TERM_L_GREEN, "Burden", "%.1y lbs", f2u(p_ptr->total_weight/10.0), END );
1575 assert(i == boundaries[2].page_rows);
1576 return ret;
1578 case 3:
1580 int i = 0;
1581 assert(ret >= boundaries[3].page_rows);
1582 ret = boundaries[3].page_rows;
1583 P_I(TERM_L_BLUE, "Armor", "[%y,%+y]", i2u(p_ptr->dis_ac), i2u(p_ptr->dis_to_a) );
1584 P_I(TERM_L_BLUE, "Fight", "(%+y,%+y)", i2u(p_ptr->dis_to_h), i2u(p_ptr->dis_to_d) );
1585 P_I(TERM_L_BLUE, "Melee", "%y", s2u(show_melee_weapon(&inventory[INVEN_WIELD])), END );
1586 P_I(TERM_L_BLUE, "Shoot", "%y", s2u(show_missile_weapon(&inventory[INVEN_BOW])), END );
1587 P_I(TERM_L_BLUE, "Blows", "%y/turn", i2u(p_ptr->num_blow), END );
1588 P_I(TERM_L_BLUE, "Shots", "%y/turn", i2u(p_ptr->num_fire), END );
1589 P_I(TERM_L_BLUE, "Infra", "%y ft", i2u(p_ptr->see_infra * 10), END );
1590 P_I(TERM_L_BLUE, "Speed", "%y", s2u(show_speed()), END );
1591 assert(i == boundaries[3].page_rows);
1592 return ret;
1594 case 4:
1596 static struct {
1597 const char *name;
1598 int skill;
1599 int div;
1600 } skills[] =
1602 { "Saving Throw", SKILL_SAV, 6 },
1603 { "Stealth", SKILL_STL, 1 },
1604 { "Fighting", SKILL_THN, 12 },
1605 { "Shooting", SKILL_THB, 12 },
1606 { "Disarming", SKILL_DIS, 8 },
1607 { "Magic Device", SKILL_DEV, 6 },
1608 { "Perception", SKILL_FOS, 6 },
1609 { "Searching", SKILL_SRH, 6 }
1611 int i;
1612 assert(N_ELEMENTS(skills) == boundaries[4].page_rows);
1613 ret = N_ELEMENTS(skills);
1614 if ((u32b) ret > size) ret = size;
1615 for (i = 0; i < ret; i++)
1617 s16b skill = p_ptr->skills[skills[i].skill];
1618 panel[i].color = TERM_L_BLUE;
1619 panel[i].label = skills[i].name;
1620 panel[i].fmt = "%y";
1621 panel[i].value[0] = s2u(likert(skill, skills[i].div, &panel[i].color));
1623 return ret;
1625 case 5:
1627 int i = 0;
1628 assert(ret >= boundaries[5].page_rows);
1629 ret = boundaries[5].page_rows;
1630 P_I(TERM_L_BLUE, "Age", "%y", i2u(p_ptr->age), END );
1631 P_I(TERM_L_BLUE, "Height", "%y", i2u(p_ptr->ht), END );
1632 P_I(TERM_L_BLUE, "Weight", "%y", i2u(p_ptr->wt), END );
1633 P_I(TERM_L_BLUE, "Status", "%y", i2u(p_ptr->sc), END );
1634 P_I(TERM_L_BLUE, "Maximize", "%y", c2u(adult_maximize ? 'Y' : 'N'), END);
1635 #if 0
1636 /* Preserve mode deleted */
1637 P_I(TERM_L_BLUE, "Preserve", "%y", c2u(adult_preserve ? 'Y' : 'N'), END);
1638 #endif
1639 assert(i == boundaries[5].page_rows);
1640 return ret;
1643 /* hopefully not reached */
1644 return 0;
1647 static void display_player_xtra_info(void)
1649 int i;
1650 int panels [] = { 1, 2, 3, 4, 5};
1651 bool left_adj [] = { 1, 0, 0, 0, 0 };
1652 data_panel data[MAX_PANEL];
1654 for (i = 0; i < (int)N_ELEMENTS(panels); i++)
1656 int oid = panels[i];
1657 int rows = get_panel(oid, data, N_ELEMENTS(data));
1659 /* Hack: Don't show 'Level' in the name, class ... panel */
1660 if (oid == 1) rows -= 1;
1662 display_panel(data, rows, left_adj[i], &boundaries[oid]);
1665 /* Indent output by 1 character, and wrap at column 72 */
1666 text_out_wrap = 72;
1667 text_out_indent = 1;
1669 /* History */
1670 Term_gotoxy(text_out_indent, 19);
1671 text_out_to_screen(TERM_WHITE, p_ptr->history);
1673 /* Reset text_out() vars */
1674 text_out_wrap = 0;
1675 text_out_indent = 0;
1677 return;
1681 * Display the character on the screen (two different modes)
1683 * The top two lines, and the bottom line (or two) are left blank.
1685 * Mode 0 = standard display with skills/history
1686 * Mode 1 = special display with equipment flags
1688 void display_player(int mode)
1690 /* Erase screen */
1691 clear_from(0);
1694 /* Stat info */
1695 display_player_stat_info();
1697 if (mode)
1699 data_panel data[MAX_PANEL];
1700 int rows = get_panel(1, data, N_ELEMENTS(data));
1702 display_panel(data, rows, 1, &boundaries[1]);
1704 /* Stat/Sustain flags */
1705 display_player_sust_info();
1707 /* Other flags */
1708 display_player_flag_info();
1711 /* Standard */
1712 else
1714 /* Extra info */
1715 display_player_xtra_info();
1721 * Hack -- Dump a character description file
1723 * XXX XXX XXX Allow the "full" flag to dump additional info,
1724 * and trigger its usage from various places in the code.
1726 errr file_character(cptr name, bool full)
1728 int i, x, y;
1730 byte a;
1731 char c;
1733 FILE *fff = NULL;
1735 store_type *st_ptr = &store[STORE_HOME];
1737 char o_name[80];
1739 char buf[1024];
1742 /* Unused parameter */
1743 (void)full;
1745 /* Build the filename */
1746 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
1748 /* File type is "TEXT" */
1749 FILE_TYPE(FILE_TYPE_TEXT);
1751 /* Check if the file currently exists */
1752 if (my_fexists(buf))
1754 char out_val[160];
1756 /* Build query */
1757 strnfmt(out_val, sizeof(out_val), "Replace existing file %s? ", buf);
1759 /* Ask */
1760 if (get_check(out_val))
1761 return -1;
1765 /* Open the file for writing */
1766 fff = my_fopen(buf, "w");
1768 /* Invalid file */
1769 if (!fff) return (-1);
1772 text_out_hook = text_out_to_file;
1773 text_out_file = fff;
1775 /* Begin dump */
1776 fprintf(fff, " [%s %s Character Dump]\n\n",
1777 VERSION_NAME, VERSION_STRING);
1780 /* Display player */
1781 display_player(0);
1783 /* Dump part of the screen */
1784 for (y = 2; y < 23; y++)
1786 /* Dump each row */
1787 for (x = 0; x < 79; x++)
1789 /* Get the attr/char */
1790 (void)(Term_what(x, y, &a, &c));
1792 /* Dump it */
1793 buf[x] = c;
1796 /* Back up over spaces */
1797 while ((x > 0) && (buf[x-1] == ' ')) --x;
1799 /* Terminate */
1800 buf[x] = '\0';
1802 /* End the row */
1803 fprintf(fff, "%s\n", buf);
1806 /* Skip a line */
1807 fprintf(fff, "\n");
1809 /* Display player */
1810 display_player(1);
1812 /* Dump part of the screen */
1813 for (y = 11; y < 20; y++)
1815 /* Dump each row */
1816 for (x = 0; x < 39; x++)
1818 /* Get the attr/char */
1819 (void)(Term_what(x, y, &a, &c));
1821 /* Dump it */
1822 buf[x] = c;
1825 /* Back up over spaces */
1826 while ((x > 0) && (buf[x-1] == ' ')) --x;
1828 /* Terminate */
1829 buf[x] = '\0';
1831 /* End the row */
1832 fprintf(fff, "%s\n", buf);
1835 /* Skip a line */
1836 fprintf(fff, "\n");
1838 /* Dump part of the screen */
1839 for (y = 11; y < 20; y++)
1841 /* Dump each row */
1842 for (x = 0; x < 39; x++)
1844 /* Get the attr/char */
1845 (void)(Term_what(x + 40, y, &a, &c));
1847 /* Dump it */
1848 buf[x] = c;
1851 /* Back up over spaces */
1852 while ((x > 0) && (buf[x-1] == ' ')) --x;
1854 /* Terminate */
1855 buf[x] = '\0';
1857 /* End the row */
1858 fprintf(fff, "%s\n", buf);
1861 /* Skip some lines */
1862 fprintf(fff, "\n\n");
1865 /* If dead, dump last messages -- Prfnoff */
1866 if (p_ptr->is_dead)
1868 i = message_num();
1869 if (i > 15) i = 15;
1870 fprintf(fff, " [Last Messages]\n\n");
1871 while (i-- > 0)
1873 fprintf(fff, "> %s\n", message_str((s16b)i));
1875 fprintf(fff, "\n\n");
1878 /* Dump the equipment */
1879 if (p_ptr->equip_cnt)
1881 fprintf(fff, " [Character Equipment]\n\n");
1882 for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
1884 object_desc(o_name, sizeof(o_name), &inventory[i], TRUE, 3);
1885 fprintf(fff, "%c) %s\n",
1886 index_to_label(i), o_name);
1888 /* Describe random object attributes */
1889 identify_random_gen(&inventory[i]);
1891 fprintf(fff, "\n\n");
1894 /* Dump the inventory */
1895 fprintf(fff, " [Character Inventory]\n\n");
1896 for (i = 0; i < INVEN_PACK; i++)
1898 if (!inventory[i].k_idx) break;
1900 object_desc(o_name, sizeof(o_name), &inventory[i], TRUE, 3);
1901 fprintf(fff, "%c) %s\n",
1902 index_to_label(i), o_name);
1904 /* Describe random object attributes */
1905 identify_random_gen(&inventory[i]);
1907 fprintf(fff, "\n\n");
1910 /* Dump the Home -- if anything there */
1911 if (st_ptr->stock_num)
1913 /* Header */
1914 fprintf(fff, " [Home Inventory]\n\n");
1916 /* Dump all available items */
1917 for (i = 0; i < st_ptr->stock_num; i++)
1919 object_desc(o_name, sizeof(o_name), &st_ptr->stock[i], TRUE, 3);
1920 fprintf(fff, "%c) %s\n", I2A(i), o_name);
1922 /* Describe random object attributes */
1923 identify_random_gen(&st_ptr->stock[i]);
1926 /* Add an empty line */
1927 fprintf(fff, "\n\n");
1931 /* Dump options */
1932 fprintf(fff, " [Options]\n\n");
1934 /* Dump options */
1935 for (i = OPT_ADULT; i < OPT_MAX; i++)
1937 if (option_desc[i])
1939 fprintf(fff, "%-45s: %s (%s)\n",
1940 option_desc[i],
1941 op_ptr->opt[i] ? "yes" : "no ",
1942 option_text[i]);
1946 /* Skip some lines */
1947 fprintf(fff, "\n\n");
1950 /* Close it */
1951 my_fclose(fff);
1954 /* Success */
1955 return (0);
1960 * Make a string lower case.
1962 static void string_lower(char *buf)
1964 char *s;
1966 /* Lowercase the string */
1967 for (s = buf; *s != 0; s++) *s = tolower((unsigned char)*s);
1972 * Recursive file perusal.
1974 * Return FALSE on "?", otherwise TRUE.
1976 * Process various special text in the input file, including the "menu"
1977 * structures used by the "help file" system.
1979 * This function could be made much more efficient with the use of "seek"
1980 * functionality, especially when moving backwards through a file, or
1981 * forwards through a file by less than a page at a time. XXX XXX XXX
1983 * Consider using a temporary file, in which special lines do not appear,
1984 * and which could be pre-padded to 80 characters per line, to allow the
1985 * use of perfect seeking. XXX XXX XXX
1987 * Allow the user to "save" the current file. XXX XXX XXX
1989 bool show_file(cptr name, cptr what, int line, int mode)
1991 int i, k, n;
1993 char ch;
1995 /* Number of "real" lines passed by */
1996 int next = 0;
1998 /* Number of "real" lines in the file */
1999 int size;
2001 /* Backup value for "line" */
2002 int back = 0;
2004 /* This screen has sub-screens */
2005 bool menu = FALSE;
2007 /* Case sensitive search */
2008 bool case_sensitive = FALSE;
2010 /* Current help file */
2011 FILE *fff = NULL;
2013 /* Find this string (if any) */
2014 char *find = NULL;
2016 /* Jump to this tag */
2017 cptr tag = NULL;
2019 /* Hold a string to find */
2020 char finder[80] = "";
2022 /* Hold a string to show */
2023 char shower[80] = "";
2025 /* Filename */
2026 char filename[1024];
2028 /* Describe this thing */
2029 char caption[128] = "";
2031 /* Path buffer */
2032 char path[1024];
2034 /* General buffer */
2035 char buf[1024];
2037 /* Lower case version of the buffer, for searching */
2038 char lc_buf[1024];
2040 /* Sub-menu information */
2041 char hook[26][32];
2043 int wid, hgt;
2047 /* Wipe the hooks */
2048 for (i = 0; i < 26; i++) hook[i][0] = '\0';
2050 /* Get size */
2051 Term_get_size(&wid, &hgt);
2053 /* Copy the filename */
2054 my_strcpy(filename, name, sizeof(filename));
2056 n = strlen(filename);
2058 /* Extract the tag from the filename */
2059 for (i = 0; i < n; i++)
2061 if (filename[i] == '#')
2063 filename[i] = '\0';
2064 tag = filename + i + 1;
2065 break;
2069 /* Redirect the name */
2070 name = filename;
2073 /* Hack XXX XXX XXX */
2074 if (what)
2076 /* Caption */
2077 my_strcpy(caption, what, sizeof(caption));
2079 /* Get the filename */
2080 my_strcpy(path, name, sizeof(path));
2082 /* Open */
2083 fff = my_fopen(path, "r");
2086 /* Look in "help" */
2087 if (!fff)
2089 /* Caption */
2090 strnfmt(caption, sizeof(caption), "Help file '%s'", name);
2092 /* Build the filename */
2093 path_build(path, sizeof(path), ANGBAND_DIR_HELP, name);
2095 /* Open the file */
2096 fff = my_fopen(path, "r");
2099 /* Look in "info" */
2100 if (!fff)
2102 /* Caption */
2103 strnfmt(caption, sizeof(caption), "Info file '%s'", name);
2105 /* Build the filename */
2106 path_build(path, sizeof(path), ANGBAND_DIR_INFO, name);
2108 /* Open the file */
2109 fff = my_fopen(path, "r");
2112 /* Oops */
2113 if (!fff)
2115 /* Message */
2116 msg_format("Cannot open '%s'.", name);
2117 message_flush();
2119 /* Oops */
2120 return (TRUE);
2124 /* Pre-Parse the file */
2125 while (TRUE)
2127 /* Read a line or stop */
2128 if (my_fgets(fff, buf, sizeof(buf))) break;
2130 /* XXX Parse "menu" items */
2131 if (prefix(buf, "***** "))
2133 char b1 = '[', b2 = ']';
2135 /* Notice "menu" requests */
2136 if ((buf[6] == b1) && isalpha((unsigned char)buf[7]) &&
2137 (buf[8] == b2) && (buf[9] == ' '))
2139 /* This is a menu file */
2140 menu = TRUE;
2142 /* Extract the menu item */
2143 k = A2I(buf[7]);
2145 /* Store the menu item (if valid) */
2146 if ((k >= 0) && (k < 26))
2147 my_strcpy(hook[k], buf + 10, sizeof(hook[0]));
2149 /* Notice "tag" requests */
2150 else if (buf[6] == '<')
2152 if (tag)
2154 /* Remove the closing '>' of the tag */
2155 buf[strlen(buf) - 1] = '\0';
2157 /* Compare with the requested tag */
2158 if (streq(buf + 7, tag))
2160 /* Remember the tagged line */
2161 line = next;
2166 /* Skip this */
2167 continue;
2170 /* Count the "real" lines */
2171 next++;
2174 /* Save the number of "real" lines */
2175 size = next;
2179 /* Display the file */
2180 while (TRUE)
2182 /* Clear screen */
2183 Term_clear();
2186 /* Restrict the visible range */
2187 if (line > (size - (hgt - 4))) line = size - (hgt - 4);
2188 if (line < 0) line = 0;
2191 /* Re-open the file if needed */
2192 if (next > line)
2194 /* Close it */
2195 my_fclose(fff);
2197 /* Hack -- Re-Open the file */
2198 fff = my_fopen(path, "r");
2200 /* Oops */
2201 if (!fff) return (TRUE);
2203 /* File has been restarted */
2204 next = 0;
2208 /* Goto the selected line */
2209 while (next < line)
2211 /* Get a line */
2212 if (my_fgets(fff, buf, sizeof(buf))) break;
2214 /* Skip tags/links */
2215 if (prefix(buf, "***** ")) continue;
2217 /* Count the lines */
2218 next++;
2222 /* Dump the next lines of the file */
2223 for (i = 0; i < hgt - 4; )
2225 /* Hack -- track the "first" line */
2226 if (!i) line = next;
2228 /* Get a line of the file or stop */
2229 if (my_fgets(fff, buf, sizeof(buf))) break;
2231 /* Hack -- skip "special" lines */
2232 if (prefix(buf, "***** ")) continue;
2234 /* Count the "real" lines */
2235 next++;
2237 /* Make a copy of the current line for searching */
2238 my_strcpy(lc_buf, buf, sizeof(lc_buf));
2240 /* Make the line lower case */
2241 if (!case_sensitive) string_lower(lc_buf);
2243 /* Hack -- keep searching */
2244 if (find && !i && !strstr(lc_buf, find)) continue;
2246 /* Hack -- stop searching */
2247 find = NULL;
2249 /* Dump the line */
2250 Term_putstr(0, i+2, -1, TERM_WHITE, buf);
2252 /* Hilite "shower" */
2253 if (shower[0])
2255 cptr str = lc_buf;
2257 /* Display matches */
2258 while ((str = strstr(str, shower)) != NULL)
2260 int len = strlen(shower);
2262 /* Display the match */
2263 Term_putstr(str-lc_buf, i+2, len, TERM_YELLOW, &buf[str-lc_buf]);
2265 /* Advance */
2266 str += len;
2270 /* Count the printed lines */
2271 i++;
2274 /* Hack -- failed search */
2275 if (find)
2277 bell("Search string not found!");
2278 line = back;
2279 find = NULL;
2280 continue;
2284 /* Show a general "title" */
2285 prt(format("[%s %s, %s, Line %d-%d/%d]", VERSION_NAME, VERSION_STRING,
2286 caption, line, line + hgt - 4, size), 0, 0);
2289 /* Prompt -- menu screen */
2290 if (menu)
2292 /* Wait for it */
2293 prt("[Press a Number, or ESC to exit.]", hgt - 1, 0);
2296 /* Prompt -- small files */
2297 else if (size <= hgt - 4)
2299 /* Wait for it */
2300 prt("[Press ESC to exit.]", hgt - 1, 0);
2303 /* Prompt -- large files */
2304 else
2306 /* Wait for it */
2307 prt("[Press Space to advance, or ESC to exit.]", hgt - 1, 0);
2310 /* Get a keypress */
2311 ch = inkey();
2313 /* Exit the help */
2314 if (ch == '?') break;
2316 /* Toggle case sensitive on/off */
2317 if (ch == '!')
2319 case_sensitive = !case_sensitive;
2322 /* Try showing */
2323 if (ch == '&')
2325 /* Get "shower" */
2326 prt("Show: ", hgt - 1, 0);
2327 (void)askfor_aux(shower, sizeof(shower), NULL);
2329 /* Make the "shower" lowercase */
2330 if (!case_sensitive) string_lower(shower);
2333 /* Try finding */
2334 if (ch == '/')
2336 /* Get "finder" */
2337 prt("Find: ", hgt - 1, 0);
2338 if (askfor_aux(finder, sizeof(finder), NULL))
2340 /* Find it */
2341 find = finder;
2342 back = line;
2343 line = line + 1;
2345 /* Make the "finder" lowercase */
2346 if (!case_sensitive) string_lower(finder);
2348 /* Show it */
2349 my_strcpy(shower, finder, sizeof(shower));
2353 /* Go to a specific line */
2354 if (ch == '#')
2356 char tmp[80] = "0";
2358 prt("Goto Line: ", hgt - 1, 0);
2359 if (askfor_aux(tmp, sizeof(tmp), NULL))
2360 line = atoi(tmp);
2363 /* Go to a specific file */
2364 if (ch == '%')
2366 char ftmp[80] = "help.hlp";
2368 prt("Goto File: ", hgt - 1, 0);
2369 if (askfor_aux(ftmp, sizeof(ftmp), NULL))
2371 if (!show_file(ftmp, NULL, 0, mode))
2372 ch = ESCAPE;
2376 /* Back up one line */
2377 if ((ch == '8') || (ch == '='))
2379 line = line - 1;
2382 /* Back up one half page */
2383 if (ch == '_')
2385 line = line - ((hgt - 4) / 2);
2388 /* Back up one full page */
2389 if ((ch == '9') || (ch == '-'))
2391 line = line - (hgt - 4);
2394 /* Back to the top */
2395 if (ch == '7')
2397 line = 0;
2400 /* Advance one line */
2401 if ((ch == '2') || (ch == '\n') || (ch == '\r'))
2403 line = line + 1;
2406 /* Advance one half page */
2407 if (ch == '+')
2409 line = line + ((hgt - 4) / 2);
2412 /* Advance one full page */
2413 if ((ch == '3') || (ch == ' '))
2415 line = line + (hgt - 4);
2418 /* Advance to the bottom */
2419 if (ch == '1')
2421 line = size;
2424 /* Recurse on letters */
2425 if (menu && isalpha((unsigned char)ch))
2427 /* Extract the requested menu item */
2428 k = A2I(ch);
2430 /* Verify the menu item */
2431 if ((k >= 0) && (k <= 25) && hook[k][0])
2433 /* Recurse on that file */
2434 if (!show_file(hook[k], NULL, 0, mode)) ch = ESCAPE;
2438 /* Exit on escape */
2439 if (ch == ESCAPE) break;
2442 /* Close the file */
2443 my_fclose(fff);
2445 /* Done */
2446 return (ch != '?');
2451 * Peruse the On-Line-Help
2453 void do_cmd_help(void)
2455 /* Save screen */
2456 screen_save();
2458 /* Peruse the main help file */
2459 (void)show_file("help.hlp", NULL, 0, 0);
2461 /* Load screen */
2462 screen_load();
2468 * Process the player name and extract a clean "base name".
2470 * If "sf" is TRUE, then we initialize "savefile" based on player name.
2472 * Some platforms (Windows, Macintosh, Amiga) leave the "savefile" empty
2473 * when a new character is created, and then when the character is done
2474 * being created, they call this function to choose a new savefile name.
2476 void process_player_name(bool sf)
2478 int i;
2481 /* Process the player name */
2482 for (i = 0; op_ptr->full_name[i]; i++)
2484 char c = op_ptr->full_name[i];
2486 /* No control characters */
2487 if (iscntrl((unsigned char)c))
2489 /* Illegal characters */
2490 quit_fmt("Illegal control char (0x%02X) in player name", c);
2493 /* Convert all non-alphanumeric symbols */
2494 if (!isalpha((unsigned char)c) && !isdigit((unsigned char)c)) c = '_';
2496 /* Build "base_name" */
2497 op_ptr->base_name[i] = c;
2500 #if defined(WINDOWS)
2502 /* Max length */
2503 if (i > 8) i = 8;
2505 #endif
2507 /* Terminate */
2508 op_ptr->base_name[i] = '\0';
2510 /* Require a "base" name */
2511 if (!op_ptr->base_name[0])
2513 my_strcpy(op_ptr->base_name, "PLAYER", sizeof(op_ptr->base_name));
2517 /* Pick savefile name if needed */
2518 if (sf)
2520 char temp[128];
2522 #if defined(SET_UID)
2523 /* Rename the savefile, using the player_uid and base_name */
2524 strnfmt(temp, sizeof(temp), "%d.%s", player_uid, op_ptr->base_name);
2525 #else
2526 /* Rename the savefile, using the base name */
2527 strnfmt(temp, sizeof(temp), "%s", op_ptr->base_name);
2528 #endif
2530 /* Build the filename */
2531 path_build(savefile, sizeof(savefile), ANGBAND_DIR_SAVE, temp);
2537 * Hack -- commit suicide
2539 void do_cmd_suicide(void)
2541 /* Flush input */
2542 flush();
2544 /* Verify Retirement */
2545 if (p_ptr->total_winner)
2547 /* Verify */
2548 if (!get_check("Do you want to retire? ")) return;
2551 /* Verify Suicide */
2552 else
2554 char ch;
2556 /* Verify */
2557 if (!get_check("Do you really want to commit suicide? ")) return;
2559 /* Special Verification for suicide */
2560 prt("Please verify SUICIDE by typing the '@' sign: ", 0, 0);
2561 flush();
2562 ch = inkey();
2563 prt("", 0, 0);
2564 if (ch != '@') return;
2567 /* Commit suicide */
2568 p_ptr->is_dead = TRUE;
2570 /* Stop playing */
2571 p_ptr->playing = FALSE;
2573 /* Leaving */
2574 p_ptr->leaving = TRUE;
2576 /* Cause of death */
2577 my_strcpy(p_ptr->died_from, "Quitting", sizeof(p_ptr->died_from));
2583 * Save the game
2585 void do_cmd_save_game(void)
2587 /* Disturb the player */
2588 disturb(1, 0);
2590 /* Clear messages */
2591 message_flush();
2593 /* Handle stuff */
2594 handle_stuff();
2596 /* Message */
2597 prt("Saving game...", 0, 0);
2599 /* Refresh */
2600 Term_fresh();
2602 /* The player is not dead */
2603 my_strcpy(p_ptr->died_from, "(saved)", sizeof(p_ptr->died_from));
2605 /* Forbid suspend */
2606 signals_ignore_tstp();
2608 /* Save the player */
2609 if (save_player())
2611 prt("Saving game... done.", 0, 0);
2614 /* Save failed (oops) */
2615 else
2617 prt("Saving game... failed!", 0, 0);
2620 /* Allow suspend again */
2621 signals_handle_tstp();
2623 /* Refresh */
2624 Term_fresh();
2626 /* Note that the player is not dead */
2627 my_strcpy(p_ptr->died_from, "(alive and well)", sizeof(p_ptr->died_from));
2633 * Hack -- Calculates the total number of points earned
2635 long total_points(void)
2637 return (p_ptr->max_exp + (100 * p_ptr->max_depth));
2643 * Centers a string within a 31 character string
2645 static void center_string(char *buf, size_t len, cptr str)
2647 int i, j;
2649 /* Total length */
2650 i = strlen(str);
2652 /* Necessary border */
2653 j = 15 - i / 2;
2655 /* Mega-Hack */
2656 strnfmt(buf, len, "%*s%s%*s", j, "", str, 31 - i - j, "");
2661 #if 0
2664 * Save a "bones" file for a dead character
2666 * Note that we will not use these files until a later version, and
2667 * then we will only use the name and level on which death occured.
2669 * Should probably attempt some form of locking...
2671 static void make_bones(void)
2673 FILE *fp;
2675 ‰ char str[1024];
2678 /* Ignore wizards and borgs */
2679 if (!(p_ptr->noscore & (NOSCORE_WIZARD | NOSCORE_BORG)))
2681 /* Ignore people who die in town */
2682 if (p_ptr->depth)
2684 char tmp[128];
2686 /* XXX XXX XXX "Bones" name */
2687 strnfmt(tmp, sizeof(tmp), "bone.%03d", p_ptr->depth);
2689 /* Build the filename */
2690 path_build(str, sizeof(str), ANGBAND_DIR_BONE, tmp);
2692 /* Attempt to open the bones file */
2693 fp = my_fopen(str, "r");
2695 /* Close it right away */
2696 if (fp) my_fclose(fp);
2698 /* Do not over-write a previous ghost */
2699 if (fp) return;
2701 /* File type is "TEXT" */
2702 FILE_TYPE(FILE_TYPE_TEXT);
2704 /* Grab permissions */
2705 safe_setuid_grab();
2707 /* Try to write a new "Bones File" */
2708 fp = my_fopen(str, "w");
2710 /* Drop permissions */
2711 safe_setuid_drop();
2713 /* Not allowed to write it? Weird. */
2714 if (!fp) return;
2716 /* Save the info */
2717 fprintf(fp, "%s\n", op_ptr->full_name);
2718 fprintf(fp, "%d\n", p_ptr->mhp);
2719 fprintf(fp, "%d\n", p_ptr->prace);
2720 fprintf(fp, "%d\n", p_ptr->pclass);
2722 /* Close and save the Bones file */
2723 my_fclose(fp);
2728 #endif /* 0 */
2732 * Hack - save the time of death
2734 static time_t death_time = (time_t)0;
2738 * Display a "tomb-stone"
2740 static void print_tomb(void)
2742 cptr p;
2744 char tmp[160];
2746 char buf[1024];
2748 FILE *fp;
2751 /* Clear screen */
2752 Term_clear();
2754 /* Build the filename */
2755 path_build(buf, sizeof(buf), ANGBAND_DIR_FILE, "dead.txt");
2757 /* Open the News file */
2758 fp = my_fopen(buf, "r");
2760 /* Dump */
2761 if (fp)
2763 int i = 0;
2765 /* Dump the file to the screen */
2766 while (0 == my_fgets(fp, buf, sizeof(buf)))
2768 /* Display and advance */
2769 put_str(buf, i++, 0);
2772 /* Close */
2773 my_fclose(fp);
2777 /* King or Queen */
2778 if (p_ptr->total_winner || (p_ptr->lev > PY_MAX_LEVEL))
2780 p = "Magnificent";
2783 /* Normal */
2784 else
2786 p = c_text + cp_ptr->title[(p_ptr->lev - 1) / 5];
2789 center_string(buf, sizeof(buf), op_ptr->full_name);
2790 put_str(buf, 6, 11);
2792 center_string(buf, sizeof(buf), "the");
2793 put_str(buf, 7, 11);
2795 center_string(buf, sizeof(buf), p);
2796 put_str(buf, 8, 11);
2799 center_string(buf, sizeof(buf), c_name + cp_ptr->name);
2800 put_str(buf, 10, 11);
2802 strnfmt(tmp, sizeof(tmp), "Level: %d", (int)p_ptr->lev);
2803 center_string(buf, sizeof(buf), tmp);
2804 put_str(buf, 11, 11);
2806 strnfmt(tmp, sizeof(tmp), "Exp: %ld", (long)p_ptr->exp);
2807 center_string(buf, sizeof(buf), tmp);
2808 put_str(buf, 12, 11);
2810 strnfmt(tmp, sizeof(tmp), "AU: %ld", (long)p_ptr->au);
2811 center_string(buf, sizeof(buf), tmp);
2812 put_str(buf, 13, 11);
2814 strnfmt(tmp, sizeof(tmp), "Killed on Level %d", p_ptr->depth);
2815 center_string(buf, sizeof(buf), tmp);
2816 put_str(buf, 14, 11);
2818 strnfmt(tmp, sizeof(tmp), "by %s.", p_ptr->died_from);
2819 center_string(buf, sizeof(buf), tmp);
2820 put_str(buf, 15, 11);
2823 strnfmt(tmp, sizeof(tmp), "%-.24s", ctime(&death_time));
2824 center_string(buf, sizeof(buf), tmp);
2825 put_str(buf, 17, 11);
2830 * Hack - Know inventory and home items upon death
2832 static void death_knowledge(void)
2834 int i;
2836 object_type *o_ptr;
2838 store_type *st_ptr = &store[STORE_HOME];
2841 /* Hack -- Know everything in the inven/equip */
2842 for (i = 0; i < INVEN_TOTAL; i++)
2844 o_ptr = &inventory[i];
2846 /* Skip non-objects */
2847 if (!o_ptr->k_idx) continue;
2849 /* Aware and Known */
2850 object_aware(o_ptr);
2851 object_known(o_ptr);
2853 /* Fully known */
2854 o_ptr->ident |= (IDENT_MENTAL);
2857 /* Hack -- Know everything in the home */
2858 for (i = 0; i < st_ptr->stock_num; i++)
2860 o_ptr = &st_ptr->stock[i];
2862 /* Skip non-objects */
2863 if (!o_ptr->k_idx) continue;
2865 /* Aware and Known */
2866 object_aware(o_ptr);
2867 object_known(o_ptr);
2869 /* Fully known */
2870 o_ptr->ident |= (IDENT_MENTAL);
2873 /* Hack -- Recalculate bonuses */
2874 p_ptr->update |= (PU_BONUS);
2876 /* Handle stuff */
2877 handle_stuff();
2882 * Display some character info
2884 static void show_info(void)
2886 int i, j, k;
2888 object_type *o_ptr;
2890 store_type *st_ptr = &store[STORE_HOME];
2893 /* Display player */
2894 display_player(0);
2896 /* Prompt for inventory */
2897 prt("Hit any key to see more information (ESC to abort): ", 23, 0);
2899 /* Allow abort at this point */
2900 if (inkey() == ESCAPE) return;
2903 /* Show equipment and inventory */
2905 /* Equipment -- if any */
2906 if (p_ptr->equip_cnt)
2908 Term_clear();
2909 item_tester_full = TRUE;
2910 show_equip();
2911 prt("You are using: -more-", 0, 0);
2912 if (inkey() == ESCAPE) return;
2915 /* Inventory -- if any */
2916 if (p_ptr->inven_cnt)
2918 Term_clear();
2919 item_tester_full = TRUE;
2920 show_inven();
2921 prt("You are carrying: -more-", 0, 0);
2922 if (inkey() == ESCAPE) return;
2927 /* Home -- if anything there */
2928 if (st_ptr->stock_num)
2930 /* Display contents of the home */
2931 for (k = 0, i = 0; i < st_ptr->stock_num; k++)
2933 /* Clear screen */
2934 Term_clear();
2936 /* Show 12 items */
2937 for (j = 0; (j < 12) && (i < st_ptr->stock_num); j++, i++)
2939 byte attr;
2941 char o_name[80];
2942 char tmp_val[80];
2944 /* Get the object */
2945 o_ptr = &st_ptr->stock[i];
2947 /* Print header, clear line */
2948 strnfmt(tmp_val, sizeof(tmp_val), "%c) ", I2A(j));
2949 prt(tmp_val, j+2, 4);
2951 /* Get the object description */
2952 object_desc(o_name, sizeof(o_name), o_ptr, TRUE, 3);
2954 /* Get the inventory color */
2955 attr = tval_to_attr[o_ptr->tval % N_ELEMENTS(tval_to_attr)];
2957 /* Display the object */
2958 c_put_str(attr, o_name, j+2, 7);
2961 /* Caption */
2962 prt(format("Your home contains (page %d): -more-", k+1), 0, 0);
2964 /* Wait for it */
2965 if (inkey() == ESCAPE) return;
2972 * Special version of 'do_cmd_examine'
2974 static void death_examine(void)
2976 int item;
2978 object_type *o_ptr;
2980 cptr q, s;
2983 /* Start out in "display" mode */
2984 p_ptr->command_see = TRUE;
2986 /* Get an item */
2987 q = "Examine which item? ";
2988 s = "You have nothing to examine.";
2990 while (TRUE)
2992 if (!get_item(&item, q, s, (USE_INVEN | USE_EQUIP))) return;
2994 /* Get the item */
2995 o_ptr = &inventory[item];
2997 /* Fully known */
2998 o_ptr->ident |= (IDENT_MENTAL);
3000 /* Describe */
3001 object_info_screen(o_ptr);
3008 * The "highscore" file descriptor, if available.
3010 static int highscore_fd = -1;
3014 * Seek score 'i' in the highscore file
3016 static int highscore_seek(int i)
3018 /* Seek for the requested record */
3019 return (fd_seek(highscore_fd, i * sizeof(high_score)));
3024 * Read one score from the highscore file
3026 static errr highscore_read(high_score *score)
3028 /* Read the record, note failure */
3029 return (fd_read(highscore_fd, (char*)(score), sizeof(high_score)));
3034 * Write one score to the highscore file
3036 static int highscore_write(const high_score *score)
3038 /* Write the record, note failure */
3039 return (fd_write(highscore_fd, (cptr)(score), sizeof(high_score)));
3046 * Just determine where a new score *would* be placed
3047 * Return the location (0 is best) or -1 on failure
3049 static int highscore_where(const high_score *score)
3051 int i;
3053 high_score the_score;
3055 /* Paranoia -- it may not have opened */
3056 if (highscore_fd < 0) return (-1);
3058 /* Go to the start of the highscore file */
3059 if (highscore_seek(0)) return (-1);
3061 /* Read until we get to a higher score */
3062 for (i = 0; i < MAX_HISCORES; i++)
3064 if (highscore_read(&the_score)) return (i);
3065 if (strcmp(the_score.pts, score->pts) < 0) return (i);
3068 /* The "last" entry is always usable */
3069 return (MAX_HISCORES - 1);
3074 * Actually place an entry into the high score file
3075 * Return the location (0 is best) or -1 on "failure"
3077 static int highscore_add(const high_score *score)
3079 int i, slot;
3080 bool done = FALSE;
3082 high_score the_score, tmpscore;
3085 /* Paranoia -- it may not have opened */
3086 if (highscore_fd < 0) return (-1);
3088 /* Determine where the score should go */
3089 slot = highscore_where(score);
3091 /* Hack -- Not on the list */
3092 if (slot < 0) return (-1);
3094 /* Hack -- prepare to dump the new score */
3095 the_score = (*score);
3097 /* Slide all the scores down one */
3098 for (i = slot; !done && (i < MAX_HISCORES); i++)
3100 /* Read the old guy, note errors */
3101 if (highscore_seek(i)) return (-1);
3102 if (highscore_read(&tmpscore)) done = TRUE;
3104 /* Back up and dump the score we were holding */
3105 if (highscore_seek(i)) return (-1);
3106 if (highscore_write(&the_score)) return (-1);
3108 /* Hack -- Save the old score, for the next pass */
3109 the_score = tmpscore;
3112 /* Return location used */
3113 return (slot);
3119 * Display the scores in a given range.
3120 * Assumes the high score list is already open.
3121 * Only five entries per line, too much info.
3123 * Mega-Hack -- allow "fake" entry at the given position.
3125 static void display_scores_aux(int from, int to, int note, high_score *score)
3127 char ch;
3129 int j, k, n, place;
3130 int count;
3132 high_score the_score;
3134 char out_val[160];
3135 char tmp_val[160];
3137 byte attr;
3140 /* Paranoia -- it may not have opened */
3141 if (highscore_fd < 0) return;
3144 /* Assume we will show the first 10 */
3145 if (from < 0) from = 0;
3146 if (to < 0) to = 10;
3147 if (to > MAX_HISCORES) to = MAX_HISCORES;
3150 /* Seek to the beginning */
3151 if (highscore_seek(0)) return;
3153 /* Hack -- Count the high scores */
3154 for (count = 0; count < MAX_HISCORES; count++)
3156 if (highscore_read(&the_score)) break;
3159 /* Hack -- allow "fake" entry to be last */
3160 if ((note == count) && score) count++;
3162 /* Forget about the last entries */
3163 if (count > to) count = to;
3166 /* Show 5 per page, until "done" */
3167 for (k = from, j = from, place = k+1; k < count; k += 5)
3169 /* Clear screen */
3170 Term_clear();
3172 /* Title */
3173 put_str(format(" %s Hall of Fame", VERSION_NAME),
3174 0, 0);
3176 /* Indicate non-top scores */
3177 if (k > 0)
3179 strnfmt(tmp_val, sizeof(tmp_val), "(from position %d)", place);
3180 put_str(tmp_val, 0, 40);
3183 /* Dump 5 entries */
3184 for (n = 0; j < count && n < 5; place++, j++, n++)
3186 int pr, pc, clev, mlev, cdun, mdun;
3188 cptr user, gold, when, aged;
3191 /* Hack -- indicate death in yellow */
3192 attr = (j == note) ? TERM_YELLOW : TERM_WHITE;
3195 /* Mega-Hack -- insert a "fake" record */
3196 if ((note == j) && score)
3198 the_score = (*score);
3199 attr = TERM_L_GREEN;
3200 score = NULL;
3201 note = -1;
3202 j--;
3205 /* Read a normal record */
3206 else
3208 /* Read the proper record */
3209 if (highscore_seek(j)) break;
3210 if (highscore_read(&the_score)) break;
3213 /* Extract the race/class */
3214 pr = atoi(the_score.p_r);
3215 pc = atoi(the_score.p_c);
3217 /* Extract the level info */
3218 clev = atoi(the_score.cur_lev);
3219 mlev = atoi(the_score.max_lev);
3220 cdun = atoi(the_score.cur_dun);
3221 mdun = atoi(the_score.max_dun);
3223 /* Hack -- extract the gold and such */
3224 for (user = the_score.uid; isspace((unsigned char)*user); user++) /* loop */;
3225 for (when = the_score.day; isspace((unsigned char)*when); when++) /* loop */;
3226 for (gold = the_score.gold; isspace((unsigned char)*gold); gold++) /* loop */;
3227 for (aged = the_score.turns; isspace((unsigned char)*aged); aged++) /* loop */;
3229 /* Clean up standard encoded form of "when" */
3230 if ((*when == '@') && strlen(when) == 9)
3232 strnfmt(tmp_val, sizeof(tmp_val),
3233 "%.4s-%.2s-%.2s",
3234 when + 1, when + 5, when + 7);
3235 when = tmp_val;
3238 /* Dump some info */
3239 strnfmt(out_val, sizeof(out_val),
3240 "%3d.%9s %s the %s %s, Level %d",
3241 place, the_score.pts, the_score.who,
3242 p_name + p_info[pr].name, c_name + c_info[pc].name,
3243 clev);
3245 /* Append a "maximum level" */
3246 if (mlev > clev) my_strcat(out_val, format(" (Max %d)", mlev), sizeof(out_val));
3248 /* Dump the first line */
3249 c_put_str(attr, out_val, n*4 + 2, 0);
3251 /* Another line of info */
3252 strnfmt(out_val, sizeof(out_val),
3253 " Killed by %s on dungeon level %d",
3254 the_score.how, cdun);
3256 /* Hack -- some people die in the town */
3257 if (!cdun)
3259 strnfmt(out_val, sizeof(out_val),
3260 " Killed by %s in the town",
3261 the_score.how);
3264 /* Append a "maximum level" */
3265 if (mdun > cdun) my_strcat(out_val, format(" (Max %d)", mdun), sizeof(out_val));
3267 /* Dump the info */
3268 c_put_str(attr, out_val, n*4 + 3, 0);
3270 /* And still another line of info */
3271 strnfmt(out_val, sizeof(out_val),
3272 " (User %s, Date %s, Gold %s, Turn %s).",
3273 user, when, gold, aged);
3274 c_put_str(attr, out_val, n*4 + 4, 0);
3278 /* Wait for response */
3279 prt("[Press ESC to exit, any other key to continue.]", 23, 17);
3280 ch = inkey();
3281 prt("", 23, 0);
3283 /* Hack -- notice Escape */
3284 if (ch == ESCAPE) break;
3290 * Hack -- Display the scores in a given range and quit.
3292 * This function is only called from "main.c" when the user asks
3293 * to see the "high scores".
3295 void display_scores(int from, int to)
3297 char buf[1024];
3299 /* Build the filename */
3300 path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw");
3302 /* Open the binary high score file, for reading */
3303 highscore_fd = fd_open(buf, O_RDONLY);
3305 /* Clear screen */
3306 Term_clear();
3308 /* Title */
3309 put_str(format(" %s Hall of Fame", VERSION_NAME), 0, 0);
3311 /* Display the scores */
3312 display_scores_aux(from, to, -1, NULL);
3314 /* Shut the high score file */
3315 fd_close(highscore_fd);
3317 /* Forget the high score fd */
3318 highscore_fd = -1;
3320 /* Wait for response */
3321 prt("[Press any key to exit.]", 23, 17);
3322 (void)inkey();
3323 prt("", 23, 0);
3325 /* Quit */
3326 quit(NULL);
3331 * Hack - save index of player's high score
3333 int score_idx = -1;
3337 * Enters a players name on a hi-score table, if "legal".
3339 * Assumes "signals_ignore_tstp()" has been called.
3341 static errr enter_score(void)
3343 int j;
3345 high_score the_score;
3348 /* No score file */
3349 if (highscore_fd < 0)
3351 return (0);
3354 /* Wizard-mode pre-empts scoring */
3355 if (p_ptr->noscore & NOSCORE_WIZARD)
3357 msg_print("Score not registered for wizards.");
3358 message_flush();
3359 score_idx = -1;
3360 return (0);
3363 #ifndef SCORE_BORGS
3365 /* Borg-mode pre-empts scoring */
3366 if (p_ptr->noscore & NOSCORE_BORG)
3368 msg_print("Score not registered for borgs.");
3369 message_flush();
3370 score_idx = -1;
3371 return (0);
3373 #endif /* SCORE_BORGS */
3375 /* Cheaters are not scored */
3376 for (j = OPT_SCORE; j < OPT_MAX; ++j)
3378 if (!op_ptr->opt[j]) continue;
3380 msg_print("Score not registered for cheaters.");
3381 message_flush();
3382 score_idx = -1;
3383 return (0);
3386 /* Hack -- Interupted */
3387 if (!p_ptr->total_winner && streq(p_ptr->died_from, "Interrupting"))
3389 msg_print("Score not registered due to interruption.");
3390 message_flush();
3391 score_idx = -1;
3392 return (0);
3395 /* Hack -- Quitter */
3396 if (!p_ptr->total_winner && streq(p_ptr->died_from, "Quitting"))
3398 msg_print("Score not registered due to quitting.");
3399 message_flush();
3400 score_idx = -1;
3401 return (0);
3405 /* Clear the record */
3406 (void)WIPE(&the_score, high_score);
3408 /* Save the version */
3409 strnfmt(the_score.what, sizeof(the_score.what), "%s", VERSION_STRING);
3411 /* Calculate and save the points */
3412 strnfmt(the_score.pts, sizeof(the_score.pts), "%9lu", (long)total_points());
3413 the_score.pts[9] = '\0';
3415 /* Save the current gold */
3416 strnfmt(the_score.gold, sizeof(the_score.gold), "%9lu", (long)p_ptr->au);
3417 the_score.gold[9] = '\0';
3419 /* Save the current turn */
3420 strnfmt(the_score.turns, sizeof(the_score.turns), "%9lu", (long)turn);
3421 the_score.turns[9] = '\0';
3423 /* Save the date in standard encoded form (9 chars) */
3424 strftime(the_score.day, sizeof(the_score.day), "@%Y%m%d", localtime(&death_time));
3426 /* Save the player name (15 chars) */
3427 strnfmt(the_score.who, sizeof(the_score.who), "%-.15s", op_ptr->full_name);
3429 /* Save the player info XXX XXX XXX */
3430 strnfmt(the_score.uid, sizeof(the_score.uid), "%7u", player_uid);
3431 strnfmt(the_score.sex, sizeof(the_score.sex), "%c", (p_ptr->psex ? 'm' : 'f'));
3432 strnfmt(the_score.p_r, sizeof(the_score.p_r), "%2d", p_ptr->prace);
3433 strnfmt(the_score.p_c, sizeof(the_score.p_c), "%2d", p_ptr->pclass);
3435 /* Save the level and such */
3436 strnfmt(the_score.cur_lev, sizeof(the_score.cur_lev), "%3d", p_ptr->lev);
3437 strnfmt(the_score.cur_dun, sizeof(the_score.cur_dun), "%3d", p_ptr->depth);
3438 strnfmt(the_score.max_lev, sizeof(the_score.max_lev), "%3d", p_ptr->max_lev);
3439 strnfmt(the_score.max_dun, sizeof(the_score.max_dun), "%3d", p_ptr->max_depth);
3441 /* Save the cause of death (31 chars) */
3442 strnfmt(the_score.how, sizeof(the_score.how), "%-.31s", p_ptr->died_from);
3444 /* Grab permissions */
3445 safe_setuid_grab();
3447 /* Lock (for writing) the highscore file, or fail */
3448 if (fd_lock(highscore_fd, F_WRLCK)) return (1);
3450 /* Drop permissions */
3451 safe_setuid_drop();
3453 /* Add a new entry to the score list, see where it went */
3454 score_idx = highscore_add(&the_score);
3456 /* Grab permissions */
3457 safe_setuid_grab();
3459 /* Unlock the highscore file, or fail */
3460 if (fd_lock(highscore_fd, F_UNLCK)) return (1);
3462 /* Drop permissions */
3463 safe_setuid_drop();
3465 /* Success */
3466 return (0);
3472 * Enters a players name on a hi-score table, if "legal", and in any
3473 * case, displays some relevant portion of the high score list.
3475 * Assumes "signals_ignore_tstp()" has been called.
3477 static void top_twenty(void)
3479 /* Clear screen */
3480 Term_clear();
3482 /* No score file */
3483 if (highscore_fd < 0)
3485 msg_print("Score file unavailable.");
3486 message_flush();
3487 return;
3490 /* Player's score unavailable */
3491 if (score_idx == -1)
3493 display_scores_aux(0, 10, -1, NULL);
3494 return;
3497 /* Hack -- Display the top fifteen scores */
3498 else if (score_idx < 10)
3500 display_scores_aux(0, 15, score_idx, NULL);
3503 /* Display the scores surrounding the player */
3504 else
3506 display_scores_aux(0, 5, score_idx, NULL);
3507 display_scores_aux(score_idx - 2, score_idx + 7, score_idx, NULL);
3511 /* Success */
3512 return;
3517 * Predict the players location, and display it.
3519 static errr predict_score(void)
3521 int j;
3523 high_score the_score;
3526 /* No score file */
3527 if (highscore_fd < 0)
3529 msg_print("Score file unavailable.");
3530 message_flush();
3531 return (0);
3535 /* Save the version */
3536 strnfmt(the_score.what, sizeof(the_score.what), "%s", VERSION_STRING);
3538 /* Calculate and save the points */
3539 strnfmt(the_score.pts, sizeof(the_score.pts), "%9lu", (long)total_points());
3541 /* Save the current gold */
3542 strnfmt(the_score.gold, sizeof(the_score.gold), "%9lu", (long)p_ptr->au);
3544 /* Save the current turn */
3545 strnfmt(the_score.turns, sizeof(the_score.turns), "%9lu", (long)turn);
3547 /* Hack -- no time needed */
3548 my_strcpy(the_score.day, "TODAY", sizeof(the_score.day));
3550 /* Save the player name (15 chars) */
3551 strnfmt(the_score.who, sizeof(the_score.who), "%-.15s", op_ptr->full_name);
3553 /* Save the player info XXX XXX XXX */
3554 strnfmt(the_score.uid, sizeof(the_score.uid), "%7u", player_uid);
3555 strnfmt(the_score.sex, sizeof(the_score.sex), "%c", (p_ptr->psex ? 'm' : 'f'));
3556 strnfmt(the_score.p_r, sizeof(the_score.p_r), "%2d", p_ptr->prace);
3557 strnfmt(the_score.p_c, sizeof(the_score.p_c), "%2d", p_ptr->pclass);
3559 /* Save the level and such */
3560 strnfmt(the_score.cur_lev, sizeof(the_score.cur_lev), "%3d", p_ptr->lev);
3561 strnfmt(the_score.cur_dun, sizeof(the_score.cur_dun), "%3d", p_ptr->depth);
3562 strnfmt(the_score.max_lev, sizeof(the_score.max_lev), "%3d", p_ptr->max_lev);
3563 strnfmt(the_score.max_dun, sizeof(the_score.max_dun), "%3d", p_ptr->max_depth);
3565 /* No cause of death */
3566 my_strcpy(the_score.how, "nobody (yet!)", sizeof(the_score.how));
3569 /* See where the entry would be placed */
3570 j = highscore_where(&the_score);
3573 /* Hack -- Display the top fifteen scores */
3574 if (j < 10)
3576 display_scores_aux(0, 15, j, &the_score);
3579 /* Display some "useful" scores */
3580 else
3582 display_scores_aux(0, 5, -1, NULL);
3583 display_scores_aux(j - 2, j + 7, j, &the_score);
3587 /* Success */
3588 return (0);
3592 void show_scores(void)
3594 char buf[1024];
3596 /* Build the filename */
3597 path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw");
3599 /* Open the binary high score file, for reading */
3600 highscore_fd = fd_open(buf, O_RDONLY);
3602 /* Paranoia -- No score file */
3603 if (highscore_fd < 0)
3605 msg_print("Score file unavailable.");
3607 else
3609 /* Save Screen */
3610 screen_save();
3612 /* Clear screen */
3613 Term_clear();
3615 /* Display the scores */
3616 if (character_generated)
3617 predict_score();
3618 else
3619 display_scores_aux(0, MAX_HISCORES, -1, NULL);
3621 /* Shut the high score file */
3622 (void)fd_close(highscore_fd);
3624 /* Forget the high score fd */
3625 highscore_fd = -1;
3627 /* Load screen */
3628 screen_load();
3630 /* Hack - Flush it */
3631 Term_fresh();
3640 * Change the player into a Winner
3642 static void kingly(void)
3644 /* Hack -- retire in town */
3645 p_ptr->depth = 0;
3647 /* Fake death */
3648 my_strcpy(p_ptr->died_from, "Ripe Old Age", sizeof(p_ptr->died_from));
3650 /* Restore the experience */
3651 p_ptr->exp = p_ptr->max_exp;
3653 /* Restore the level */
3654 p_ptr->lev = p_ptr->max_lev;
3656 /* Hack -- Instant Gold */
3657 p_ptr->au += 10000000L;
3659 /* Clear screen */
3660 Term_clear();
3662 /* Display a crown */
3663 put_str("#", 1, 34);
3664 put_str("#####", 2, 32);
3665 put_str("#", 3, 34);
3666 put_str(",,, $$$ ,,,", 4, 28);
3667 put_str(",,=$ \"$$$$$\" $=,,", 5, 24);
3668 put_str(",$$ $$$ $$,", 6, 22);
3669 put_str("*> <*> <*", 7, 22);
3670 put_str("$$ $$$ $$", 8, 22);
3671 put_str("\"$$ $$$ $$\"", 9, 22);
3672 put_str("\"$$ $$$ $$\"", 10, 23);
3673 put_str("*#########*#########*", 11, 24);
3674 put_str("*#########*#########*", 12, 24);
3676 /* Display a message */
3677 put_str("Veni, Vidi, Vici!", 15, 26);
3678 put_str("I came, I saw, I conquered!", 16, 21);
3679 put_str(format("All Hail the Mighty %s!", sp_ptr->winner), 17, 22);
3681 /* Flush input */
3682 flush();
3684 /* Wait for response */
3685 pause_line(Term->hgt - 1);
3690 * Handle character death
3692 static void close_game_aux(void)
3694 int ch;
3695 bool wants_to_quit = FALSE;
3696 cptr p = "[(i)nformation, (m)essages, (f)ile dump, (v)iew scores, e(x)amine item, ESC]";
3699 /* Handle retirement */
3700 if (p_ptr->total_winner) kingly();
3702 /* Save dead player */
3703 if (!save_player())
3705 msg_print("death save failed!");
3706 message_flush();
3709 /* Get time of death */
3710 (void)time(&death_time);
3712 /* You are dead */
3713 print_tomb();
3715 /* Hack - Know everything upon death */
3716 death_knowledge();
3718 /* Enter player in high score list */
3719 enter_score();
3721 /* Flush all input keys */
3722 flush();
3724 /* Flush messages */
3725 message_flush();
3727 /* Loop */
3728 while (!wants_to_quit)
3730 /* Describe options */
3731 Term_putstr(1, 23, -1, TERM_WHITE, p);
3733 /* Query */
3734 ch = inkey();
3736 switch (ch)
3738 /* Exit */
3739 case ESCAPE:
3741 if (get_check("Do you want to quit? "))
3742 wants_to_quit = TRUE;
3744 break;
3747 /* File dump */
3748 case 'f':
3749 case 'F':
3751 char ftmp[80];
3753 strnfmt(ftmp, sizeof(ftmp), "%s.txt", op_ptr->base_name);
3755 if (get_string("File name: ", ftmp, sizeof(ftmp)))
3757 if (ftmp[0] && (ftmp[0] != ' '))
3759 errr err;
3761 /* Save screen */
3762 screen_save();
3764 /* Dump a character file */
3765 err = file_character(ftmp, FALSE);
3767 /* Load screen */
3768 screen_load();
3770 /* Check result */
3771 if (err)
3773 msg_print("Character dump failed!");
3775 else
3777 msg_print("Character dump successful.");
3780 /* Flush messages */
3781 message_flush();
3785 break;
3788 /* Show more info */
3789 case 'i':
3790 case 'I':
3792 /* Save screen */
3793 screen_save();
3795 /* Show the character */
3796 show_info();
3798 /* Load screen */
3799 screen_load();
3801 break;
3804 /* Show last messages */
3805 case 'm':
3806 case 'M':
3808 /* Save screen */
3809 screen_save();
3811 /* Display messages */
3812 do_cmd_messages();
3814 /* Load screen */
3815 screen_load();
3817 break;
3820 /* Show top scores */
3821 case 'v':
3822 case 'V':
3824 /* Save screen */
3825 screen_save();
3827 /* Show the scores */
3828 top_twenty();
3830 /* Load screen */
3831 screen_load();
3833 break;
3836 /* Examine an item */
3837 case 'x':
3838 case 'X':
3840 /* Save screen */
3841 screen_save();
3843 /* Clear the screen */
3844 Term_clear();
3846 /* Examine items */
3847 death_examine();
3849 /* Load screen */
3850 screen_load();
3852 break;
3857 #if 0
3858 /* Dump bones file */
3859 make_bones();
3860 #endif
3865 * Close up the current game (player may or may not be dead)
3867 * Note that the savefile is not saved until the tombstone is
3868 * actually displayed and the player has a chance to examine
3869 * the inventory and such. This allows cheating if the game
3870 * is equipped with a "quit without save" method. XXX XXX XXX
3872 void close_game(void)
3874 char buf[1024];
3877 /* Handle stuff */
3878 handle_stuff();
3880 /* Flush the messages */
3881 message_flush();
3883 /* Flush the input */
3884 flush();
3887 /* No suspending now */
3888 signals_ignore_tstp();
3891 /* Hack -- Increase "icky" depth */
3892 character_icky++;
3895 /* Build the filename */
3896 path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw");
3898 /* Grab permissions */
3899 safe_setuid_grab();
3901 /* Open the high score file, for reading/writing */
3902 highscore_fd = fd_open(buf, O_RDWR);
3904 /* Drop permissions */
3905 safe_setuid_drop();
3907 /* Handle death */
3908 if (p_ptr->is_dead)
3910 /* Auxiliary routine */
3911 close_game_aux();
3914 /* Still alive */
3915 else
3917 /* Save the game */
3918 do_cmd_save_game();
3920 if(Term->mapped_flag) {
3921 /* Prompt for scores XXX XXX XXX */
3922 prt("Press Return (or Escape).", 0, 40);
3924 /* Predict score (or ESCAPE) */
3925 if (inkey() != ESCAPE) predict_score();
3930 /* Shut the high score file */
3931 fd_close(highscore_fd);
3933 /* Forget the high score fd */
3934 highscore_fd = -1;
3937 /* Hack -- Decrease "icky" depth */
3938 character_icky--;
3941 /* Allow suspending now */
3942 signals_handle_tstp();
3947 * Handle abrupt death of the visual system
3949 * This routine is called only in very rare situations, and only
3950 * by certain visual systems, when they experience fatal errors.
3952 * XXX XXX Hack -- clear the death flag when creating a HANGUP
3953 * save file so that player can see tombstone when restart.
3955 void exit_game_panic(void)
3957 /* If nothing important has happened, just quit */
3958 if (!character_generated || character_saved) quit("panic");
3960 /* Mega-Hack -- see "msg_print()" */
3961 msg_flag = FALSE;
3963 /* Clear the top line */
3964 prt("", 0, 0);
3966 /* Hack -- turn off some things */
3967 disturb(1, 0);
3969 /* Hack -- Delay death XXX XXX XXX */
3970 if (p_ptr->chp < 0) p_ptr->is_dead = FALSE;
3972 /* Hardcode panic save */
3973 p_ptr->panic_save = 1;
3975 /* Forbid suspend */
3976 signals_ignore_tstp();
3978 /* Indicate panic save */
3979 my_strcpy(p_ptr->died_from, "(panic save)", sizeof(p_ptr->died_from));
3981 /* Panic save, or get worried */
3982 if (!save_player()) quit("panic save failed!");
3984 /* Successful panic save */
3985 quit("panic save succeeded!");
3991 static void write_html_escape_char(FILE *htm, char c)
3993 switch (c)
3995 case '<':
3996 fprintf(htm, "&lt;");
3997 break;
3998 case '>':
3999 fprintf(htm, "&gt;");
4000 break;
4001 case '&':
4002 fprintf(htm, "&amp;");
4003 break;
4004 default:
4005 fprintf(htm, "%c", c);
4006 break;
4011 /* Take an html screenshot */
4012 void html_screenshot(cptr name, int mode)
4014 int y, x;
4015 int wid, hgt;
4017 byte a = TERM_WHITE;
4018 byte oa = TERM_WHITE;
4019 char c = ' ';
4021 const char *new_color_fmt = (mode == 0) ?
4022 "<font color=\"#%02X%02X%02X\">"
4023 : "[COLOR=\"#%02X%02X%02X\"]";
4024 const char *change_color_fmt = (mode == 0) ?
4025 "</font><font color=\"#%02X%02X%02X\">"
4026 : "[/COLOR][COLOR=\"#%02X%02X%02X\"]";
4027 const char *close_color_fmt = mode == 0 ? "</font>" : "[/COLOR]";
4029 FILE *htm;
4031 char buf[1024];
4033 /* Build the filename */
4034 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, name);
4036 /* File type is "TEXT" */
4037 FILE_TYPE(FILE_TYPE_TEXT);
4039 /* Append to the file */
4040 htm = my_fopen(buf, "w");
4042 /* Oops */
4043 if (!htm)
4045 plog_fmt("Cannot write the '%s' file!", buf);
4046 return;
4049 /* Retrieve current screen size */
4050 Term_get_size(&wid, &hgt);
4052 if(mode == 0)
4054 fprintf(htm, "<!DOCTYPE html><html><head>\n");
4055 fprintf(htm, " <meta='generator' content='%s %s'>\n",
4056 VERSION_NAME, VERSION_STRING);
4057 fprintf(htm, " <title>%s</title>\n", name);
4058 fprintf(htm, "</head>\n\n");
4059 fprintf(htm, "<body style='color: #fff; background: #000;'>\n");
4060 fprintf(htm, "<pre>\n");
4062 else
4064 fprintf(htm, "[CODE][TT][BC=black][COLOR=white]\n");
4067 /* Dump the screen */
4068 for (y = 0; y < hgt; y++)
4070 for (x = 0; x < wid; x++)
4072 /* Get the attr/char */
4073 (void)(Term_what(x, y, &a, &c));
4075 /* Color change */
4076 if (oa != a && c != ' ')
4078 /* From the default white to another color */
4079 if (oa == TERM_WHITE)
4081 fprintf(htm, new_color_fmt,
4082 angband_color_table[a][1],
4083 angband_color_table[a][2],
4084 angband_color_table[a][3]);
4086 /* From another color to the default white */
4087 else if (a == TERM_WHITE)
4089 fprintf(htm, close_color_fmt);
4091 /* Change colors */
4092 else
4094 fprintf(htm, change_color_fmt,
4095 angband_color_table[a][1],
4096 angband_color_table[a][2],
4097 angband_color_table[a][3]);
4100 /* Remember the last color */
4101 oa = a;
4104 /* Write the character and escape special HTML characters */
4105 if (mode == 0) write_html_escape_char(htm, c);
4106 else fprintf(htm, "%c", c);
4109 /* End the row */
4110 fprintf(htm, "\n");
4113 /* Close the last font-color tag if necessary */
4114 if (oa != TERM_WHITE) fprintf(htm, close_color_fmt);
4116 if (mode == 0)
4118 fprintf(htm, "</pre>\n");
4119 fprintf(htm, "</body>\n");
4120 fprintf(htm, "</html>\n");
4122 else
4124 fprintf(htm, "[/COLOR][/BC][/TT][/CODE]\n");
4127 /* Close it */
4128 my_fclose(htm);