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.
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
)
46 /* Skip leading whitespace */
47 for ( ; *s
&& isspace((unsigned char)*s
); ++s
) /* loop */;
52 /* Find next whitespace, if any */
53 for (t
= s
; *t
&& !isspace((unsigned char)*t
); ++t
) /* loop */;
55 /* Nuke and advance (if necessary) */
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
)
100 /* Scan the string */
103 /* Found a delimiter */
104 if ((*t
== ':') || (*t
== '/')) break;
106 /* Handle single quotes */
112 /* Handle backslash */
115 /* Require a character */
121 /* Hack -- Require a close quote */
122 if (*t
!= '\'') *t
= '\'';
125 /* Handle back-slash */
132 /* Nuke and advance */
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.
175 * Specify the attr/char values for "objects" by kind index.
178 * Specify the attr/char values for "features" by feature index.
181 * Specify the attr/char values for "special" things.
184 * Specify the attribute values for inventory "objects" by kind tval.
187 * Define a macro action, given an encoded macro action.
190 * Create a macro, given an encoded macro trigger.
193 * Create a keymap, given an encoded keymap trigger.
196 * Turn an option off, given its name.
199 * Turn an option on, given its name.
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.
211 * Specify the attr/char values for "flavors" by flavors index.
214 errr
process_pref_file_command(char *buf
)
221 /* Skip "empty" lines */
222 if (!buf
[0]) return (0);
224 /* Skip "blank" lines */
225 if (isspace((unsigned char)buf
[0])) return (0);
228 if (buf
[0] == '#') return (0);
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 */
242 if (tokenize(buf
+2, 3, zz
) == 3)
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);
250 if (n1
) r_ptr
->x_attr
= (byte
)n1
;
251 if (n2
) r_ptr
->x_char
= (char)n2
;
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]);
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
);
274 n1
= strtol(zz
[0], NULL
, 0);
275 n2
= strtol(zz
[1], NULL
, 0);
276 squelch_level
[n1
] = n2
;
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
;
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
;
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)
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);
315 if (n1
) k_ptr
->x_attr
= (byte
)n1
;
316 if (n2
) k_ptr
->x_char
= (char)n2
;
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)
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);
333 if (n1
) f_ptr
->x_attr
= (byte
)n1
;
334 if (n2
) f_ptr
->x_char
= (char)n2
;
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
;
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
;
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
;
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);
395 /* Process "P:<str>" -- create macro */
396 else if (buf
[0] == 'P')
399 text_to_ascii(tmp
, sizeof(tmp
), buf
+2);
400 macro_add(tmp
, macro_buffer
);
404 /* Process "C:<num>:<str>" -- create keymap */
405 else if (buf
[0] == 'C')
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);
420 string_free(keymap_act
[mode
][i
]);
422 keymap_act
[mode
][i
] = string_make(macro_buffer
);
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);
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')
450 tok
= tokenize(buf
+ 2, MAX_MACRO_MOD
+ 2, zz
);
452 /* Trigger template */
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 */
467 /* One modifier-character per modifier */
468 if (num
+ 2 != tok
) return 1;
471 macro_template
= string_make(zz
[0]);
474 macro_modifier_chr
= string_make(zz
[1]);
477 for (i
= 0; i
< num
; i
++)
479 macro_modifier_name
[i
] = string_make(zz
[2+i
]);
489 if (max_macrotrigger
>= MAX_MACRO_TRIGGER
)
491 msg_print("Too many macro triggers!");
495 /* Buffer for the trigger name */
496 C_MAKE(buf
, strlen(zz
[0]) + 1, char);
498 /* Simulate strcpy() and skip the '\' escape character */
508 /* Terminate the trigger name */
511 /* Store the trigger name */
512 macro_trigger_name
[max_macrotrigger
] = string_make(buf
);
514 /* Free the buffer */
518 macro_trigger_keycode
[0][max_macrotrigger
] = string_make(zz
[1]);
520 /* Special shifted keycode */
523 macro_trigger_keycode
[1][max_macrotrigger
] = string_make(zz
[2]);
525 /* Shifted keycode is the same as the normal keycode */
528 macro_trigger_keycode
[1][max_macrotrigger
] = string_make(zz
[1]);
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
;
551 /* Ignore unknown options */
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
;
568 /* Ignore unknown options */
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
])
597 op_ptr
->window_flag
[win
] |= (1L << flag
);
602 op_ptr
->window_flag
[win
] &= ~(1L << flag
);
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
));
635 * Helper function for "process_pref_file()"
638 * v: output buffer array
644 static cptr
process_pref_file_expr(char **sp
, char *fp
)
660 while (isspace((unsigned char)*s
)) s
++;
678 t
= process_pref_file_expr(&s
, &f
);
687 else if (streq(t
, "IOR"))
690 while (*s
&& (f
!= b2
))
692 t
= process_pref_file_expr(&s
, &f
);
693 if (*t
&& !streq(t
, "0")) v
= "1";
698 else if (streq(t
, "AND"))
701 while (*s
&& (f
!= b2
))
703 t
= process_pref_file_expr(&s
, &f
);
704 if (*t
&& streq(t
, "0")) v
= "0";
709 else if (streq(t
, "NOT"))
712 while (*s
&& (f
!= b2
))
714 t
= process_pref_file_expr(&s
, &f
);
715 if (*t
&& !streq(t
, "0")) v
= "0";
720 else if (streq(t
, "EQU"))
725 t
= process_pref_file_expr(&s
, &f
);
727 while (*s
&& (f
!= b2
))
730 t
= process_pref_file_expr(&s
, &f
);
731 if (*t
&& !streq(p
, t
)) v
= "0";
736 else if (streq(t
, "LEQ"))
741 t
= process_pref_file_expr(&s
, &f
);
743 while (*s
&& (f
!= b2
))
746 t
= process_pref_file_expr(&s
, &f
);
747 if (*t
&& (strcmp(p
, t
) >= 0)) v
= "0";
752 else if (streq(t
, "GEQ"))
757 t
= process_pref_file_expr(&s
, &f
);
759 while (*s
&& (f
!= b2
))
762 t
= process_pref_file_expr(&s
, &f
);
763 if (*t
&& (strcmp(p
, t
) <= 0)) v
= "0";
770 while (*s
&& (f
!= b2
))
772 t
= process_pref_file_expr(&s
, &f
);
777 if (f
!= b2
) v
= "?x?x?";
779 /* Extract final and Terminate */
780 if ((f
= *s
) != '\0') *s
++ = '\0';
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';
796 if (streq(b
+1, "SYS"))
802 else if (streq(b
+1, "GRAF"))
808 else if (streq(b
+1, "RACE"))
810 v
= p_name
+ rp_ptr
->name
;
814 else if (streq(b
+1, "CLASS"))
816 v
= c_name
+ cp_ptr
->name
;
820 else if (streq(b
+1, "PLAYER"))
822 v
= op_ptr
->base_name
;
826 else if (streq(b
+1, "VERSION"))
851 * Open the "user pref file" and parse it.
853 static errr
process_pref_file_aux(cptr name
)
869 fp
= my_fopen(name
, "r");
872 if (!fp
) return (-1);
875 /* Process the file */
876 while (0 == my_fgets(fp
, buf
, sizeof(buf
)))
882 /* Skip "empty" lines */
883 if (!buf
[0]) continue;
885 /* Skip "blank" lines */
886 if (isspace((unsigned char)buf
[0])) continue;
889 if (buf
[0] == '#') continue;
893 my_strcpy(old
, buf
, sizeof(old
));
896 /* Process "?:<expr>" */
897 if ((buf
[0] == '?') && (buf
[1] == ':'))
907 v
= process_pref_file_expr(&s
, &f
);
910 bypass
= (streq(v
, "0") ? TRUE
: FALSE
);
916 /* Apply conditionals */
917 if (bypass
) continue;
920 /* Process "%:<file>" */
923 /* Process that file if allowed */
924 (void)process_pref_file(buf
+ 2);
931 /* Process the line */
932 err
= process_pref_file_command(buf
);
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
);
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
)
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 */
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
);
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
)
1003 /* Negative value */
1007 return ("Very Bad");
1010 /* Analyze the value */
1027 *attr
= TERM_YELLOW
;
1032 *attr
= TERM_YELLOW
;
1037 *attr
= TERM_YELLOW
;
1038 return ("Very Good");
1043 *attr
= TERM_L_GREEN
;
1044 return ("Excellent");
1052 *attr
= TERM_L_GREEN
;
1060 *attr
= TERM_L_GREEN
;
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
)
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
);
1095 static void display_player_equippy(int y
, int x
)
1105 /* Dump equippy chars */
1106 for (i
= INVEN_WIELD
; i
< INVEN_TOTAL
; ++i
)
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
);
1119 Term_putch(x
+i
-INVEN_WIELD
, y
, a
, c
);
1124 * 'Database' of resistances and abilities to display
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
)
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};
1201 object_flags_known(o_ptr
, &f
[1], &f
[2], &f
[3]);
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@");
1216 display_player_equippy(row
++, col
+6);
1219 static void display_player_flag_info(void)
1222 for (i
= 0; i
< 4; i
++)
1224 display_resistance_panel(player_flag_table
+(i
*RES_ROWS
), RES_ROWS
+3,
1231 * Special display, part 2b
1233 static void display_player_stat_info(void)
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
++)
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
);
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);
1281 strnfmt(buf
, sizeof(buf
), "%+3d", rp_ptr
->r_adj
[i
]);
1282 c_put_str(TERM_L_BLUE
, buf
, row
+i
, col
+12);
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
;
1323 u32b ignore_f2
, ignore_f3
;
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
++)
1364 if (o_ptr
->pval
> 0)
1370 if (o_ptr
->pval
< 10) c
= I2D(o_ptr
->pval
);
1374 if (o_ptr
->pval
< 0)
1380 if (o_ptr
->pval
> -10) c
= I2D(-(o_ptr
->pval
));
1390 /* Convert '.' to 's' */
1391 if (c
== '.') c
= 's';
1394 /* Dump proper character */
1395 Term_putch(col
, row
+stat
, a
, c
);
1403 player_flags(&f1
, &f2
, &f3
);
1406 for (stat
= 0; stat
< A_MAX
; ++stat
)
1415 /* Dark green "s" */
1421 Term_putch(col
, row
+stat
, a
, c
);
1428 c_put_str(TERM_WHITE
, "abcdefghijkl@", row
+6, col
);
1431 display_player_equippy(row
+7, col
);
1435 static const region boundaries
[] =
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)
1450 return "[=-WIZARD-=]";
1451 else if (p_ptr
->total_winner
|| p_ptr
->lev
> PY_MAX_LEVEL
)
1452 return "***WINNER***";
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
);
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);
1481 strnfmt(buffer
, sizeof(buffer
), "Lev %d", p_ptr
->max_depth
);
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);
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
))
1510 strnfmt(buffer
, sizeof(buffer
), "(%+d,%+d)", hit
, dam
);
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
;
1520 if (object_known_p(o_ptr
))
1526 strnfmt(buffer
, sizeof(buffer
), "(%+d,%+d)", hit
, dam
);
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; \
1541 int get_panel(int oid
, data_panel
*panel
, size_t size
)
1543 int ret
= (s32b
) size
;
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
);
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
);
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
);
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 }
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
));
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
);
1636 /* Preserve mode deleted */
1637 P_I(TERM_L_BLUE
, "Preserve", "%y", c2u(adult_preserve
? 'Y' : 'N'), END
);
1639 assert(i
== boundaries
[5].page_rows
);
1643 /* hopefully not reached */
1647 static void display_player_xtra_info(void)
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 */
1667 text_out_indent
= 1;
1670 Term_gotoxy(text_out_indent
, 19);
1671 text_out_to_screen(TERM_WHITE
, p_ptr
->history
);
1673 /* Reset text_out() vars */
1675 text_out_indent
= 0;
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
)
1695 display_player_stat_info();
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();
1708 display_player_flag_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
)
1735 store_type
*st_ptr
= &store
[STORE_HOME
];
1742 /* Unused parameter */
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
))
1757 strnfmt(out_val
, sizeof(out_val
), "Replace existing file %s? ", buf
);
1760 if (get_check(out_val
))
1765 /* Open the file for writing */
1766 fff
= my_fopen(buf
, "w");
1769 if (!fff
) return (-1);
1772 text_out_hook
= text_out_to_file
;
1773 text_out_file
= fff
;
1776 fprintf(fff
, " [%s %s Character Dump]\n\n",
1777 VERSION_NAME
, VERSION_STRING
);
1780 /* Display player */
1783 /* Dump part of the screen */
1784 for (y
= 2; y
< 23; y
++)
1787 for (x
= 0; x
< 79; x
++)
1789 /* Get the attr/char */
1790 (void)(Term_what(x
, y
, &a
, &c
));
1796 /* Back up over spaces */
1797 while ((x
> 0) && (buf
[x
-1] == ' ')) --x
;
1803 fprintf(fff
, "%s\n", buf
);
1809 /* Display player */
1812 /* Dump part of the screen */
1813 for (y
= 11; y
< 20; y
++)
1816 for (x
= 0; x
< 39; x
++)
1818 /* Get the attr/char */
1819 (void)(Term_what(x
, y
, &a
, &c
));
1825 /* Back up over spaces */
1826 while ((x
> 0) && (buf
[x
-1] == ' ')) --x
;
1832 fprintf(fff
, "%s\n", buf
);
1838 /* Dump part of the screen */
1839 for (y
= 11; y
< 20; y
++)
1842 for (x
= 0; x
< 39; x
++)
1844 /* Get the attr/char */
1845 (void)(Term_what(x
+ 40, y
, &a
, &c
));
1851 /* Back up over spaces */
1852 while ((x
> 0) && (buf
[x
-1] == ' ')) --x
;
1858 fprintf(fff
, "%s\n", buf
);
1861 /* Skip some lines */
1862 fprintf(fff
, "\n\n");
1865 /* If dead, dump last messages -- Prfnoff */
1870 fprintf(fff
, " [Last Messages]\n\n");
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
)
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");
1932 fprintf(fff
, " [Options]\n\n");
1935 for (i
= OPT_ADULT
; i
< OPT_MAX
; i
++)
1939 fprintf(fff
, "%-45s: %s (%s)\n",
1941 op_ptr
->opt
[i
] ? "yes" : "no ",
1946 /* Skip some lines */
1947 fprintf(fff
, "\n\n");
1960 * Make a string lower case.
1962 static void string_lower(char *buf
)
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
)
1995 /* Number of "real" lines passed by */
1998 /* Number of "real" lines in the file */
2001 /* Backup value for "line" */
2004 /* This screen has sub-screens */
2007 /* Case sensitive search */
2008 bool case_sensitive
= FALSE
;
2010 /* Current help file */
2013 /* Find this string (if any) */
2016 /* Jump to this tag */
2019 /* Hold a string to find */
2020 char finder
[80] = "";
2022 /* Hold a string to show */
2023 char shower
[80] = "";
2026 char filename
[1024];
2028 /* Describe this thing */
2029 char caption
[128] = "";
2034 /* General buffer */
2037 /* Lower case version of the buffer, for searching */
2040 /* Sub-menu information */
2047 /* Wipe the hooks */
2048 for (i
= 0; i
< 26; i
++) hook
[i
][0] = '\0';
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
] == '#')
2064 tag
= filename
+ i
+ 1;
2069 /* Redirect the name */
2073 /* Hack XXX XXX XXX */
2077 my_strcpy(caption
, what
, sizeof(caption
));
2079 /* Get the filename */
2080 my_strcpy(path
, name
, sizeof(path
));
2083 fff
= my_fopen(path
, "r");
2086 /* Look in "help" */
2090 strnfmt(caption
, sizeof(caption
), "Help file '%s'", name
);
2092 /* Build the filename */
2093 path_build(path
, sizeof(path
), ANGBAND_DIR_HELP
, name
);
2096 fff
= my_fopen(path
, "r");
2099 /* Look in "info" */
2103 strnfmt(caption
, sizeof(caption
), "Info file '%s'", name
);
2105 /* Build the filename */
2106 path_build(path
, sizeof(path
), ANGBAND_DIR_INFO
, name
);
2109 fff
= my_fopen(path
, "r");
2116 msg_format("Cannot open '%s'.", name
);
2124 /* Pre-Parse the file */
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 */
2142 /* Extract the menu item */
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] == '<')
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 */
2170 /* Count the "real" lines */
2174 /* Save the number of "real" lines */
2179 /* Display the file */
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 */
2197 /* Hack -- Re-Open the file */
2198 fff
= my_fopen(path
, "r");
2201 if (!fff
) return (TRUE
);
2203 /* File has been restarted */
2208 /* Goto the selected line */
2212 if (my_fgets(fff
, buf
, sizeof(buf
))) break;
2214 /* Skip tags/links */
2215 if (prefix(buf
, "***** ")) continue;
2217 /* Count the lines */
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 */
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 */
2250 Term_putstr(0, i
+2, -1, TERM_WHITE
, buf
);
2252 /* Hilite "shower" */
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
]);
2270 /* Count the printed lines */
2274 /* Hack -- failed search */
2277 bell("Search string not found!");
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 */
2293 prt("[Press a Number, or ESC to exit.]", hgt
- 1, 0);
2296 /* Prompt -- small files */
2297 else if (size
<= hgt
- 4)
2300 prt("[Press ESC to exit.]", hgt
- 1, 0);
2303 /* Prompt -- large files */
2307 prt("[Press Space to advance, or ESC to exit.]", hgt
- 1, 0);
2310 /* Get a keypress */
2314 if (ch
== '?') break;
2316 /* Toggle case sensitive on/off */
2319 case_sensitive
= !case_sensitive
;
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
);
2337 prt("Find: ", hgt
- 1, 0);
2338 if (askfor_aux(finder
, sizeof(finder
), NULL
))
2345 /* Make the "finder" lowercase */
2346 if (!case_sensitive
) string_lower(finder
);
2349 my_strcpy(shower
, finder
, sizeof(shower
));
2353 /* Go to a specific line */
2358 prt("Goto Line: ", hgt
- 1, 0);
2359 if (askfor_aux(tmp
, sizeof(tmp
), NULL
))
2363 /* Go to a specific file */
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
))
2376 /* Back up one line */
2377 if ((ch
== '8') || (ch
== '='))
2382 /* Back up one half page */
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 */
2400 /* Advance one line */
2401 if ((ch
== '2') || (ch
== '\n') || (ch
== '\r'))
2406 /* Advance one half page */
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 */
2424 /* Recurse on letters */
2425 if (menu
&& isalpha((unsigned char)ch
))
2427 /* Extract the requested menu item */
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 */
2451 * Peruse the On-Line-Help
2453 void do_cmd_help(void)
2458 /* Peruse the main help file */
2459 (void)show_file("help.hlp", NULL
, 0, 0);
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
)
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)
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 */
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
);
2526 /* Rename the savefile, using the base name */
2527 strnfmt(temp
, sizeof(temp
), "%s", op_ptr
->base_name
);
2530 /* Build the filename */
2531 path_build(savefile
, sizeof(savefile
), ANGBAND_DIR_SAVE
, temp
);
2537 * Hack -- commit suicide
2539 void do_cmd_suicide(void)
2544 /* Verify Retirement */
2545 if (p_ptr
->total_winner
)
2548 if (!get_check("Do you want to retire? ")) return;
2551 /* Verify Suicide */
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);
2564 if (ch
!= '@') return;
2567 /* Commit suicide */
2568 p_ptr
->is_dead
= TRUE
;
2571 p_ptr
->playing
= FALSE
;
2574 p_ptr
->leaving
= TRUE
;
2576 /* Cause of death */
2577 my_strcpy(p_ptr
->died_from
, "Quitting", sizeof(p_ptr
->died_from
));
2585 void do_cmd_save_game(void)
2587 /* Disturb the player */
2590 /* Clear messages */
2597 prt("Saving game...", 0, 0);
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 */
2611 prt("Saving game... done.", 0, 0);
2614 /* Save failed (oops) */
2617 prt("Saving game... failed!", 0, 0);
2620 /* Allow suspend again */
2621 signals_handle_tstp();
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
)
2652 /* Necessary border */
2656 strnfmt(buf
, len
, "%*s%s%*s", j
, "", str
, 31 - i
- j
, "");
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)
2678 /* Ignore wizards and borgs */
2679 if (!(p_ptr
->noscore
& (NOSCORE_WIZARD
| NOSCORE_BORG
)))
2681 /* Ignore people who die in town */
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 */
2701 /* File type is "TEXT" */
2702 FILE_TYPE(FILE_TYPE_TEXT
);
2704 /* Grab permissions */
2707 /* Try to write a new "Bones File" */
2708 fp
= my_fopen(str
, "w");
2710 /* Drop permissions */
2713 /* Not allowed to write it? Weird. */
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 */
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)
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");
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);
2778 if (p_ptr
->total_winner
|| (p_ptr
->lev
> PY_MAX_LEVEL
))
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)
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
);
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
);
2870 o_ptr
->ident
|= (IDENT_MENTAL
);
2873 /* Hack -- Recalculate bonuses */
2874 p_ptr
->update
|= (PU_BONUS
);
2882 * Display some character info
2884 static void show_info(void)
2890 store_type
*st_ptr
= &store
[STORE_HOME
];
2893 /* Display player */
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
)
2909 item_tester_full
= TRUE
;
2911 prt("You are using: -more-", 0, 0);
2912 if (inkey() == ESCAPE
) return;
2915 /* Inventory -- if any */
2916 if (p_ptr
->inven_cnt
)
2919 item_tester_full
= TRUE
;
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
++)
2937 for (j
= 0; (j
< 12) && (i
< st_ptr
->stock_num
); j
++, i
++)
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);
2962 prt(format("Your home contains (page %d): -more-", k
+1), 0, 0);
2965 if (inkey() == ESCAPE
) return;
2972 * Special version of 'do_cmd_examine'
2974 static void death_examine(void)
2983 /* Start out in "display" mode */
2984 p_ptr
->command_see
= TRUE
;
2987 q
= "Examine which item? ";
2988 s
= "You have nothing to examine.";
2992 if (!get_item(&item
, q
, s
, (USE_INVEN
| USE_EQUIP
))) return;
2995 o_ptr
= &inventory
[item
];
2998 o_ptr
->ident
|= (IDENT_MENTAL
);
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
)
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
)
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 */
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
)
3132 high_score the_score
;
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)
3173 put_str(format(" %s Hall of Fame", VERSION_NAME
),
3176 /* Indicate non-top scores */
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
;
3205 /* Read a normal record */
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
),
3234 when
+ 1, when
+ 5, when
+ 7);
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
,
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 */
3259 strnfmt(out_val
, sizeof(out_val
),
3260 " Killed by %s in the town",
3264 /* Append a "maximum level" */
3265 if (mdun
> cdun
) my_strcat(out_val
, format(" (Max %d)", mdun
), sizeof(out_val
));
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);
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
)
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
);
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 */
3320 /* Wait for response */
3321 prt("[Press any key to exit.]", 23, 17);
3331 * Hack - save index of player's high score
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)
3345 high_score the_score
;
3349 if (highscore_fd
< 0)
3354 /* Wizard-mode pre-empts scoring */
3355 if (p_ptr
->noscore
& NOSCORE_WIZARD
)
3357 msg_print("Score not registered for wizards.");
3365 /* Borg-mode pre-empts scoring */
3366 if (p_ptr
->noscore
& NOSCORE_BORG
)
3368 msg_print("Score not registered for borgs.");
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.");
3386 /* Hack -- Interupted */
3387 if (!p_ptr
->total_winner
&& streq(p_ptr
->died_from
, "Interrupting"))
3389 msg_print("Score not registered due to interruption.");
3395 /* Hack -- Quitter */
3396 if (!p_ptr
->total_winner
&& streq(p_ptr
->died_from
, "Quitting"))
3398 msg_print("Score not registered due to quitting.");
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 */
3447 /* Lock (for writing) the highscore file, or fail */
3448 if (fd_lock(highscore_fd
, F_WRLCK
)) return (1);
3450 /* Drop permissions */
3453 /* Add a new entry to the score list, see where it went */
3454 score_idx
= highscore_add(&the_score
);
3456 /* Grab permissions */
3459 /* Unlock the highscore file, or fail */
3460 if (fd_lock(highscore_fd
, F_UNLCK
)) return (1);
3462 /* Drop permissions */
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)
3483 if (highscore_fd
< 0)
3485 msg_print("Score file unavailable.");
3490 /* Player's score unavailable */
3491 if (score_idx
== -1)
3493 display_scores_aux(0, 10, -1, NULL
);
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 */
3506 display_scores_aux(0, 5, score_idx
, NULL
);
3507 display_scores_aux(score_idx
- 2, score_idx
+ 7, score_idx
, NULL
);
3517 * Predict the players location, and display it.
3519 static errr
predict_score(void)
3523 high_score the_score
;
3527 if (highscore_fd
< 0)
3529 msg_print("Score file unavailable.");
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 */
3576 display_scores_aux(0, 15, j
, &the_score
);
3579 /* Display some "useful" scores */
3582 display_scores_aux(0, 5, -1, NULL
);
3583 display_scores_aux(j
- 2, j
+ 7, j
, &the_score
);
3592 void show_scores(void)
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.");
3615 /* Display the scores */
3616 if (character_generated
)
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 */
3630 /* Hack - Flush it */
3640 * Change the player into a Winner
3642 static void kingly(void)
3644 /* Hack -- retire in town */
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;
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);
3684 /* Wait for response */
3685 pause_line(Term
->hgt
- 1);
3690 * Handle character death
3692 static void close_game_aux(void)
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 */
3705 msg_print("death save failed!");
3709 /* Get time of death */
3710 (void)time(&death_time
);
3715 /* Hack - Know everything upon death */
3718 /* Enter player in high score list */
3721 /* Flush all input keys */
3724 /* Flush messages */
3728 while (!wants_to_quit
)
3730 /* Describe options */
3731 Term_putstr(1, 23, -1, TERM_WHITE
, p
);
3741 if (get_check("Do you want to quit? "))
3742 wants_to_quit
= TRUE
;
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] != ' '))
3764 /* Dump a character file */
3765 err
= file_character(ftmp
, FALSE
);
3773 msg_print("Character dump failed!");
3777 msg_print("Character dump successful.");
3780 /* Flush messages */
3788 /* Show more info */
3795 /* Show the character */
3804 /* Show last messages */
3811 /* Display messages */
3820 /* Show top scores */
3827 /* Show the scores */
3836 /* Examine an item */
3843 /* Clear the screen */
3858 /* Dump bones file */
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)
3880 /* Flush the messages */
3883 /* Flush the input */
3887 /* No suspending now */
3888 signals_ignore_tstp();
3891 /* Hack -- Increase "icky" depth */
3895 /* Build the filename */
3896 path_build(buf
, sizeof(buf
), ANGBAND_DIR_APEX
, "scores.raw");
3898 /* Grab permissions */
3901 /* Open the high score file, for reading/writing */
3902 highscore_fd
= fd_open(buf
, O_RDWR
);
3904 /* Drop permissions */
3910 /* Auxiliary routine */
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 */
3937 /* Hack -- Decrease "icky" depth */
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()" */
3963 /* Clear the top line */
3966 /* Hack -- turn off some things */
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
)
3996 fprintf(htm
, "<");
3999 fprintf(htm
, ">");
4002 fprintf(htm
, "&");
4005 fprintf(htm
, "%c", c
);
4011 /* Take an html screenshot */
4012 void html_screenshot(cptr name
, int mode
)
4017 byte a
= TERM_WHITE
;
4018 byte oa
= TERM_WHITE
;
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]";
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");
4045 plog_fmt("Cannot write the '%s' file!", buf
);
4049 /* Retrieve current screen size */
4050 Term_get_size(&wid
, &hgt
);
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");
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
));
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
);
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 */
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
);
4113 /* Close the last font-color tag if necessary */
4114 if (oa
!= TERM_WHITE
) fprintf(htm
, close_color_fmt
);
4118 fprintf(htm
, "</pre>\n");
4119 fprintf(htm
, "</body>\n");
4120 fprintf(htm
, "</html>\n");
4124 fprintf(htm
, "[/COLOR][/BC][/TT][/CODE]\n");