2 * @brief ウィザードモードの処理(スポイラー出力中心) / Spoiler generation -BEN-
5 * Copyright (c) 1997 Ben Harrison, and others
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 * 2013 Deskull rearranged comment for Doxygen.
12 #include "wizard/wizard-spoiler.h"
13 #include "flavor/flavor-describer.h"
14 #include "flavor/object-flavor-types.h"
15 #include "floor/floor-town.h"
16 #include "inventory/inventory-slot-types.h"
17 #include "io/files-util.h"
18 #include "io/input-key-acceptor.h"
19 #include "main/sound-of-music.h"
20 #include "monster-race/monster-race.h"
21 #include "monster-race/race-flags1.h"
22 #include "monster-race/race-flags7.h"
23 #include "object-enchant/object-ego.h"
24 #include "object-enchant/special-object-flags.h"
25 #include "object-enchant/tr-types.h"
26 #include "object-enchant/trc-types.h"
27 #include "object-enchant/trg-types.h"
28 #include "object/object-flags.h"
29 #include "object/object-generator.h"
30 #include "object/object-info.h"
31 #include "object/object-kind-hook.h"
32 #include "object/object-kind.h"
33 #include "object/object-value.h"
34 #include "perception/object-perception.h"
35 #include "store/store-util.h"
36 #include "store/store.h"
37 #include "system/angband-version.h"
38 #include "system/artifact-type-definition.h"
39 #include "term/screen-processor.h"
40 #include "term/term-color-types.h"
41 #include "util/angband-files.h"
42 #include "util/bit-flags-calculator.h"
43 #include "util/int-char-converter.h"
44 #include "util/quarks.h"
45 #include "util/sort.h"
46 #include "view/display-lore.h"
47 #include "view/display-messages.h"
48 #include "wizard/spoiler-table.h"
50 /* ITEM_SEP separates items within a list */
53 /* LIST_SEP separates lists */
54 #define LIST_SEP _(',', ';')
56 #define MAX_EVOL_DEPTH 64
59 * These are used to format the artifact spoiler file. INDENT1 is used
60 * to indent all but the first line of an artifact spoiler. INDENT2 is
61 * used when a line "wraps". (Bladeturner's resistances cause this.)
66 /* MAX_LINE_LEN specifies when a line should wrap. */
67 #define MAX_LINE_LEN 75
69 /* Returns a "+" string if a number is non-negative and an empty string if negative */
70 #define POSITIZE(v) (((v) >= 0) ? "+" : "")
72 /* Given an array, determine how many elements are in the array */
73 #define N_ELEMENTS(a) (sizeof(a) / sizeof((a)[0]))
75 /* A special type used just for deailing with pvals */
76 typedef struct pval_info_type {
77 char pval_desc[12]; /* This will contain a string such as "+2", "-10", etc. */
79 /* A list of various player traits affected by an object's pval such as stats, speed, stealth, etc. */
80 concptr pval_affects[N_ELEMENTS(stat_flags_desc) - 1 + N_ELEMENTS(pval_flags1_desc) + 1];
83 typedef struct obj_desc_list {
84 char description[MAX_NLEN]; /* "The Longsword Dragonsmiter (6d4) (+20, +25)" */
85 pval_info_type pval_info; /* Description of what is affected by an object's pval */
86 concptr slays[N_ELEMENTS(slay_flags_desc) + 1]; /* A list of an object's slaying preferences */
87 concptr brands[N_ELEMENTS(brand_flags_desc) + 1]; /* A list if an object's elemental brands */
88 concptr immunities[N_ELEMENTS(immune_flags_desc) + 1]; /* A list of immunities granted by an object */
89 concptr resistances[N_ELEMENTS(resist_flags_desc) + 1]; /* A list of resistances granted by an object */
90 concptr sustains[N_ELEMENTS(sustain_flags_desc) - 1 + 1]; /* A list of stats sustained by an object */
92 /* A list of various magical qualities an object may have */
93 concptr misc_magic[N_ELEMENTS(misc_flags2_desc) + N_ELEMENTS(misc_flags3_desc) + 1 /* Permanent Light */
95 + 1 /* type of curse */
96 + 1]; /* sentinel NULL */
98 char addition[80]; /* Additional ability or resistance */
99 concptr activation; /* A string describing an artifact's activation */
100 char misc_desc[80]; /* "Level 20, Rarity 30, 3.0 lbs, 20000 Gold" */
104 * The spoiler file being created
106 static FILE *spoiler_file = NULL;
109 * @brief シンボル職の記述名を返す /
110 * Extract a textual representation of an attribute
111 * @param r_ptr モンスター種族の構造体ポインタ
114 static concptr attr_to_text(monster_race *r_ptr)
116 if (r_ptr->flags1 & RF1_ATTR_CLEAR)
117 return _("透明な", "Clear");
119 if (r_ptr->flags1 & RF1_ATTR_MULTI)
120 return _("万色の", "Multi");
122 if (r_ptr->flags1 & RF1_ATTR_SEMIRAND)
123 return _("準ランダムな", "S.Rand");
125 switch (r_ptr->d_attr) {
127 return _("黒い", "Dark");
129 return _("白い", "White");
131 return _("青灰色の", "Slate");
133 return _("オレンジの", "Orange");
135 return _("赤い", "Red");
137 return _("緑の", "Green");
139 return _("青い", "Blue");
141 return _("琥珀色の", "Umber");
143 return _("灰色の", "L.Dark");
145 return _("明るい青灰色の", "L.Slate");
147 return _("紫の", "Violet");
149 return _("黄色の", "Yellow");
151 return _("明るい赤の", "L.Red");
153 return _("明るい緑の", "L.Green");
155 return _("明るい青の", "L.Blue");
157 return _("明るい琥珀色の", "L.Umber");
160 return _("変な色の", "Icky");
164 * @brief ベースアイテムの各情報を文字列化する /
166 * @param player_ptr プレーヤーへの参照ポインタ
167 * @param buf 名称を返すバッファ参照ポインタ
168 * @param dam ダメージダイス記述を返すバッファ参照ポインタ
169 * @param wgt 重量記述を返すバッファ参照ポインタ
170 * @param lev 生成階記述を返すバッファ参照ポインタ
171 * @param chance 生成機会を返すバッファ参照ポインタ
172 * @param val 価値を返すバッファ参照ポインタ
176 static void kind_info(player_type *player_ptr, char *buf, char *dam, char *wgt, char *chance, DEPTH *lev, PRICE *val, OBJECT_IDX k)
183 object_prep(player_ptr, q_ptr, k);
184 q_ptr->ident |= (IDENT_KNOWN);
190 (*lev) = k_info[q_ptr->k_idx].level;
191 (*val) = object_value(player_ptr, q_ptr);
192 if (!buf || !dam || !chance || !wgt)
195 describe_flavor(player_ptr, buf, q_ptr, (OD_NAME_ONLY | OD_STORE));
197 switch (q_ptr->tval) {
204 sprintf(dam, "%dd%d", q_ptr->dd, q_ptr->ds);
211 sprintf(dam, "%dd%d", q_ptr->dd, q_ptr->ds);
222 case TV_DRAG_ARMOR: {
223 sprintf(dam, "%d", q_ptr->ac);
229 for (i = 0; i < 4; i++) {
230 char chance_aux[20] = "";
231 if (k_info[q_ptr->k_idx].chance[i] > 0) {
232 sprintf(chance_aux, "%s%3dF:%+4d", (i != 0 ? "/" : ""), (int)k_info[q_ptr->k_idx].locale[i], 100 / k_info[q_ptr->k_idx].chance[i]);
233 strcat(chance, chance_aux);
237 sprintf(wgt, "%3d.%d", (int)(q_ptr->weight / 10), (int)(q_ptr->weight % 10));
241 * @brief 各ベースアイテムの情報を一行毎に記述する /
242 * @param player_ptr プレーヤーへの参照ポインタ
243 * Create a spoiler file for items
247 static void spoil_obj_desc(player_type *player_ptr, concptr fname)
249 int i, k, s, t, n = 0, group_start = 0;
256 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
257 spoiler_file = angband_fopen(buf, "w");
260 msg_print("Cannot create spoiler file.");
264 fprintf(spoiler_file, "Spoiler File -- Basic Items (Hengband %d.%d.%d.%d)\n\n\n", FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH, FAKE_VER_EXTRA);
265 fprintf(spoiler_file, "%-37s%8s%7s%5s %40s%9s\n", "Description", "Dam/AC", "Wgt", "Lev", "Chance", "Cost");
266 fprintf(spoiler_file, "%-37s%8s%7s%5s %40s%9s\n", "-------------------------------------", "------", "---", "---", "----------------", "----");
267 for (i = 0; TRUE; i++) {
268 if (group_item[i].name) {
270 for (s = 0; s < n - 1; s++) {
271 for (t = 0; t < n - 1; t++) {
281 kind_info(player_ptr, NULL, NULL, NULL, NULL, &e1, &t1, who[i1]);
282 kind_info(player_ptr, NULL, NULL, NULL, NULL, &e2, &t2, who[i2]);
284 if ((t1 > t2) || ((t1 == t2) && (e1 > e2))) {
292 fprintf(spoiler_file, "\n\n%s\n\n", group_item[group_start].name);
293 for (s = 0; s < n; s++) {
296 kind_info(player_ptr, buf, dam, wgt, chance, &e, &v, who[s]);
297 fprintf(spoiler_file, " %-35s%8s%7s%5d %-40s%9ld\n", buf, dam, wgt, (int)e, chance, (long)(v));
303 if (!group_item[i].tval)
309 for (k = 1; k < max_k_idx; k++) {
310 object_kind *k_ptr = &k_info[k];
311 if ((k_ptr->tval != group_item[i].tval) || (k_ptr->gen_flags & TRG_INSTA_ART))
318 if (ferror(spoiler_file) || angband_fclose(spoiler_file)) {
319 msg_print("Cannot close spoiler file.");
323 msg_print("Successfully created a spoiler file.");
327 * @brief ファイルポインタ先に同じ文字を複数出力する /
328 * Write out `n' of the character `c' to the spoiler file
333 static void spoiler_out_n_chars(int n, char c)
336 fputc(c, spoiler_file);
340 * @brief ファイルポインタ先に改行を複数出力する /
341 * Write out `n' blank lines to the spoiler file
345 static void spoiler_blanklines(int n) { spoiler_out_n_chars(n, '\n'); }
348 * @brief ファイルポインタ先に複数のハイフンで装飾した文字列を出力する /
349 * Write a line to the spoiler file and then "underline" it with hypens
350 * @param str 出力したい文字列
353 static void spoiler_underline(concptr str)
355 fprintf(spoiler_file, "%s\n", str);
356 spoiler_out_n_chars(strlen(str), '-');
357 fprintf(spoiler_file, "\n");
361 * @brief アーティファクトの特性一覧を出力する /
362 * Write a line to the spoiler file and then "underline" it with hypens
363 * @param art_flags アーティファクトのフラグ群
364 * @param flag_ptr フラグ記述情報の参照ポインタ
365 * @param desc_ptr 記述内容を返すための文字列参照ポインタ
366 * @param n_elmnts フラグの要素数
367 * @return desc_ptrと同じアドレス
370 * This function does most of the actual "analysis". Given a set of bit flags
371 * (which will be from one of the flags fields from the object in question),
372 * a "flag description structure", a "description list", and the number of
373 * elements in the "flag description structure", this function sets the
374 * "description list" members to the appropriate descriptions contained in
375 * the "flag description structure".
376 * The possibly updated description pointer is returned.
379 static concptr *spoiler_flag_aux(const BIT_FLAGS art_flags[TR_FLAG_SIZE], const flag_desc *flag_ptr, concptr *desc_ptr, const int n_elmnts)
381 for (int i = 0; i < n_elmnts; ++i) {
382 if (have_flag(art_flags, flag_ptr[i].flag)) {
383 *desc_ptr++ = flag_ptr[i].desc;
391 * @brief アイテムの特定記述内容を返す /
392 * Acquire a "basic" description "The Cloak of Death [1,+10]"
393 * @param o_ptr 記述を得たいオブジェクトの参照ポインタ
394 * @param desc_ptr 記述内容を返すための文字列参照ポインタ
397 static void analyze_general(player_type *player_ptr, object_type *o_ptr, char *desc_ptr)
399 describe_flavor(player_ptr, desc_ptr, o_ptr, (OD_NAME_AND_ENCHANT | OD_STORE));
403 * @brief アーティファクトがプレイヤーに与えるpval修正を構造体に収める /
404 * List "player traits" altered by an artifact's pval. These include stats,
405 * speed, infravision, tunneling, stealth, searching, and extra attacks.
406 * @param o_ptr オブジェクト構造体の参照ポインタ
407 * @param pi_ptr pval修正構造体の参照ポインタ
410 static void analyze_pval(player_type *player_ptr, object_type *o_ptr, pval_info_type *pi_ptr)
412 BIT_FLAGS flgs[TR_FLAG_SIZE];
413 concptr *affects_list;
415 pi_ptr->pval_desc[0] = '\0';
419 object_flags(player_ptr, o_ptr, flgs);
420 affects_list = pi_ptr->pval_affects;
421 sprintf(pi_ptr->pval_desc, "%s%d", POSITIZE(o_ptr->pval), o_ptr->pval);
422 if (have_flag(flgs, TR_STR) && have_flag(flgs, TR_INT) && have_flag(flgs, TR_WIS) && have_flag(flgs, TR_DEX) && have_flag(flgs, TR_CON)
423 && have_flag(flgs, TR_CHR)) {
424 *affects_list++ = _("全能力", "All stats");
425 } else if (have_flag(flgs, TR_STR) || have_flag(flgs, TR_INT) || have_flag(flgs, TR_WIS) || have_flag(flgs, TR_DEX) || have_flag(flgs, TR_CON)
426 || have_flag(flgs, TR_CHR)) {
427 affects_list = spoiler_flag_aux(flgs, stat_flags_desc, affects_list, N_ELEMENTS(stat_flags_desc));
430 affects_list = spoiler_flag_aux(flgs, pval_flags1_desc, affects_list, N_ELEMENTS(pval_flags1_desc));
431 *affects_list = NULL;
435 * @brief アーティファクトの種族スレイ特性を構造体に収める /
436 * Note the slaying specialties of a weapon
437 * @param o_ptr オブジェクト構造体の参照ポインタ
438 * @param slay_list 種族スレイ構造体の参照ポインタ
441 static void analyze_slay(player_type *player_ptr, object_type *o_ptr, concptr *slay_list)
443 BIT_FLAGS flgs[TR_FLAG_SIZE];
444 object_flags(player_ptr, o_ptr, flgs);
445 slay_list = spoiler_flag_aux(flgs, slay_flags_desc, slay_list, N_ELEMENTS(slay_flags_desc));
450 * @brief アーティファクトの属性ブランド特性を構造体に収める /
451 * Note an object's elemental brands
452 * @param o_ptr オブジェクト構造体の参照ポインタ
453 * @param brand_list 属性ブランド構造体の参照ポインタ
456 static void analyze_brand(player_type *player_ptr, object_type *o_ptr, concptr *brand_list)
458 BIT_FLAGS flgs[TR_FLAG_SIZE];
459 object_flags(player_ptr, o_ptr, flgs);
460 brand_list = spoiler_flag_aux(flgs, brand_flags_desc, brand_list, N_ELEMENTS(brand_flags_desc));
465 * @brief アーティファクトの通常耐性を構造体に収める /
466 * Note an object's elemental brands
467 * @param o_ptr オブジェクト構造体の参照ポインタ
468 * @param resist_list 通常耐性構造体の参照ポインタ
471 static void analyze_resist(player_type *player_ptr, object_type *o_ptr, concptr *resist_list)
473 BIT_FLAGS flgs[TR_FLAG_SIZE];
474 object_flags(player_ptr, o_ptr, flgs);
475 resist_list = spoiler_flag_aux(flgs, resist_flags_desc, resist_list, N_ELEMENTS(resist_flags_desc));
480 * @brief アーティファクトの免疫特性を構造体に収める /
481 * Note the immunities granted by an object
482 * @param o_ptr オブジェクト構造体の参照ポインタ
483 * @param immune_list 免疫構造体の参照ポインタ
486 static void analyze_immune(player_type *player_ptr, object_type *o_ptr, concptr *immune_list)
488 BIT_FLAGS flgs[TR_FLAG_SIZE];
489 object_flags(player_ptr, o_ptr, flgs);
490 immune_list = spoiler_flag_aux(flgs, immune_flags_desc, immune_list, N_ELEMENTS(immune_flags_desc));
495 * @brief アーティファクトの維持特性を構造体に収める /
496 * Note which stats an object sustains
497 * @param o_ptr オブジェクト構造体の参照ポインタ
498 * @param sustain_list 維持特性構造体の参照ポインタ
501 static void analyze_sustains(player_type *player_ptr, object_type *o_ptr, concptr *sustain_list)
503 BIT_FLAGS flgs[TR_FLAG_SIZE];
504 object_flags(player_ptr, o_ptr, flgs);
505 if (have_flag(flgs, TR_SUST_STR) && have_flag(flgs, TR_SUST_INT) && have_flag(flgs, TR_SUST_WIS) && have_flag(flgs, TR_SUST_DEX)
506 && have_flag(flgs, TR_SUST_CON) && have_flag(flgs, TR_SUST_CHR)) {
507 *sustain_list++ = _("全能力", "All stats");
508 } else if (have_flag(flgs, TR_SUST_STR) || have_flag(flgs, TR_SUST_INT) || have_flag(flgs, TR_SUST_WIS) || have_flag(flgs, TR_SUST_DEX)
509 || have_flag(flgs, TR_SUST_CON) || have_flag(flgs, TR_SUST_CHR)) {
510 sustain_list = spoiler_flag_aux(flgs, sustain_flags_desc, sustain_list, N_ELEMENTS(sustain_flags_desc));
513 *sustain_list = NULL;
517 * @brief アーティファクトのその他の特性を構造体に収める /
518 * Note miscellaneous powers bestowed by an artifact such as see invisible,
519 * free action, permanent light, etc.
520 * @param o_ptr オブジェクト構造体の参照ポインタ
521 * @param misc_list その他の特性構造体の参照ポインタ
524 static void analyze_misc_magic(player_type *player_ptr, object_type *o_ptr, concptr *misc_list)
526 BIT_FLAGS flgs[TR_FLAG_SIZE];
529 object_flags(player_ptr, o_ptr, flgs);
530 misc_list = spoiler_flag_aux(flgs, misc_flags2_desc, misc_list, N_ELEMENTS(misc_flags2_desc));
531 misc_list = spoiler_flag_aux(flgs, misc_flags3_desc, misc_list, N_ELEMENTS(misc_flags3_desc));
533 if (have_flag(flgs, TR_LITE_1))
535 if (have_flag(flgs, TR_LITE_2))
537 if (have_flag(flgs, TR_LITE_3))
539 if (have_flag(flgs, TR_LITE_M1))
541 if (have_flag(flgs, TR_LITE_M2))
543 if (have_flag(flgs, TR_LITE_M3))
546 if (o_ptr->name2 == EGO_LITE_SHINE)
549 if (have_flag(flgs, TR_LITE_FUEL)) {
551 sprintf(desc, _("それは燃料補給によって明かり(半径 %d)を授ける。", "It provides light (radius %d) when fueled."), (int)rad);
554 sprintf(desc, _("永久光源(半径 %d)", "Permanent Light(radius %d)"), (int)rad);
556 sprintf(desc, _("永久光源(半径-%d)。", "Permanent Light(radius -%d)"), (int)-rad);
560 *misc_list++ = quark_str(quark_add(desc));
562 if (have_flag(flgs, TR_TY_CURSE))
563 *misc_list++ = _("太古の怨念", "Ancient Curse");
565 if (o_ptr->curse_flags & TRC_PERMA_CURSE)
566 *misc_list++ = _("永遠の呪い", "Permanently Cursed");
567 else if (o_ptr->curse_flags & TRC_HEAVY_CURSE)
568 *misc_list++ = _("強力な呪い", "Heavily Cursed");
569 else if (o_ptr->curse_flags & TRC_CURSED)
570 *misc_list++ = _("呪い", "Cursed");
572 if (have_flag(flgs, TR_ADD_L_CURSE))
573 *misc_list++ = _("呪いを増やす", "Cursing");
575 if (have_flag(flgs, TR_ADD_H_CURSE))
576 *misc_list++ = _("強力な呪いを増やす", "Heavily Cursing");
582 * @brief アーティファクトの追加ランダム特性を構造体に収める /
583 * Note additional ability and/or resistance of fixed artifacts
584 * @param o_ptr オブジェクト構造体の参照ポインタ
585 * @param addition 追加ランダム耐性構造体の参照ポインタ
588 static void analyze_addition(object_type *o_ptr, char *addition)
590 artifact_type *a_ptr = &a_info[o_ptr->name1];
591 strcpy(addition, "");
592 if ((a_ptr->gen_flags & TRG_XTRA_POWER) && (a_ptr->gen_flags & TRG_XTRA_H_RES))
593 strcat(addition, _("能力and耐性", "Ability and Resistance"));
594 else if (a_ptr->gen_flags & TRG_XTRA_POWER) {
595 strcat(addition, _("能力", "Ability"));
596 if (a_ptr->gen_flags & TRG_XTRA_RES_OR_POWER)
597 strcat(addition, _("(1/2でand耐性)", "(plus Resistance about 1/2)"));
598 } else if (a_ptr->gen_flags & TRG_XTRA_H_RES) {
599 strcat(addition, _("耐性", "Resistance"));
600 if (a_ptr->gen_flags & TRG_XTRA_RES_OR_POWER)
601 strcat(addition, _("(1/2でand能力)", "(plus Ability about 1/2)"));
602 } else if (a_ptr->gen_flags & TRG_XTRA_RES_OR_POWER)
603 strcat(addition, _("能力or耐性", "Ability or Resistance"));
607 * @brief アーティファクトの基本情報を文字列に収める /
608 * Determine the minimum depth an artifact can appear, its rarity, its weight,
609 * and its value in gold pieces
610 * @param o_ptr オブジェクト構造体の参照ポインタ
611 * @param misc_desc 基本情報を収める文字列参照ポインタ
614 static void analyze_misc(object_type *o_ptr, char *misc_desc)
616 artifact_type *a_ptr = &a_info[o_ptr->name1];
617 sprintf(misc_desc, _("レベル %d, 希少度 %u, %d.%d kg, $%ld", "Level %d, Rarity %u, %d.%d lbs, %ld Gold"), (int)a_ptr->level, a_ptr->rarity,
618 _(lbtokg1(a_ptr->weight), a_ptr->weight / 10), _(lbtokg2(a_ptr->weight), a_ptr->weight % 10), (long int)a_ptr->cost);
622 * @brief アーティファクトの情報全体を構造体に収める /
623 * Fill in an object description structure for a given object
624 * and its value in gold pieces
625 * @param player_ptr プレーヤーへの参照ポインタ
626 * @param o_ptr オブジェクト構造体の参照ポインタ
627 * @param desc_ptr 全アーティファクト情報を収める文字列参照ポインタ
630 static void object_analyze(player_type *player_ptr, object_type *o_ptr, obj_desc_list *desc_ptr)
632 analyze_general(player_ptr, o_ptr, desc_ptr->description);
633 analyze_pval(player_ptr, o_ptr, &desc_ptr->pval_info);
634 analyze_brand(player_ptr, o_ptr, desc_ptr->brands);
635 analyze_slay(player_ptr, o_ptr, desc_ptr->slays);
636 analyze_immune(player_ptr, o_ptr, desc_ptr->immunities);
637 analyze_resist(player_ptr, o_ptr, desc_ptr->resistances);
638 analyze_sustains(player_ptr, o_ptr, desc_ptr->sustains);
639 analyze_misc_magic(player_ptr, o_ptr, desc_ptr->misc_magic);
640 analyze_addition(o_ptr, desc_ptr->addition);
641 analyze_misc(o_ptr, desc_ptr->misc_desc);
642 desc_ptr->activation = activation_explanation(player_ptr, o_ptr);
646 * @brief バッファにアーティファクト出力情報ヘッダを収める /
649 static void print_header(void)
653 sprintf(buf, "Artifact Spoilers for Hengband Version %d.%d.%d", FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
654 spoiler_underline(buf);
658 * @brief フラグ名称を出力する汎用関数
659 * @param header ヘッダに出力するフラグ群の名前
660 * @param list フラグ名リスト
661 * @param separator フラグ表示の区切り記号
664 static void spoiler_outlist(concptr header, concptr *list, char separator)
667 char line[MAX_LINE_LEN + 1], buf[80];
672 strcpy(line, INDENT1);
673 if (header && (header[0])) {
674 strcat(line, header);
678 int line_len = strlen(line);
681 buf_len = strlen(buf);
683 sprintf(buf + buf_len, "%c ", separator);
687 if (line_len + buf_len <= MAX_LINE_LEN) {
691 if (line_len > 1 && line[line_len - 1] == ' ' && line[line_len - 2] == LIST_SEP) {
692 line[line_len - 2] = '\0';
693 fprintf(spoiler_file, "%s\n", line);
694 sprintf(line, "%s%s", INDENT1, buf);
696 fprintf(spoiler_file, "%s\n", line);
697 sprintf(line, "%s%s", INDENT2, buf);
700 line_len = strlen(line);
707 fprintf(spoiler_file, "%s\n", line);
711 * @brief アーティファクト一件をスポイラー出力する /
712 * Create a spoiler file entry for an artifact
713 * @param art_ptr アーティファクト情報をまとめた構造体の参照ポインタ
716 static void spoiler_print_art(obj_desc_list *art_ptr)
718 pval_info_type *pval_ptr = &art_ptr->pval_info;
720 fprintf(spoiler_file, "%s\n", art_ptr->description);
721 if (pval_ptr->pval_desc[0]) {
722 sprintf(buf, _("%sの修正:", "%s to"), pval_ptr->pval_desc);
723 spoiler_outlist(buf, pval_ptr->pval_affects, ITEM_SEP);
726 spoiler_outlist(_("対:", "Slay"), art_ptr->slays, ITEM_SEP);
727 spoiler_outlist(_("武器属性:", ""), art_ptr->brands, LIST_SEP);
728 spoiler_outlist(_("免疫:", "Immunity to"), art_ptr->immunities, ITEM_SEP);
729 spoiler_outlist(_("耐性:", "Resist"), art_ptr->resistances, ITEM_SEP);
730 spoiler_outlist(_("維持:", "Sustain"), art_ptr->sustains, ITEM_SEP);
731 spoiler_outlist("", art_ptr->misc_magic, LIST_SEP);
733 if (art_ptr->addition[0])
734 fprintf(spoiler_file, _("%s追加: %s\n", "%sAdditional %s\n"), INDENT1, art_ptr->addition);
736 if (art_ptr->activation)
737 fprintf(spoiler_file, _("%s発動: %s\n", "%sActivates for %s\n"), INDENT1, art_ptr->activation);
739 fprintf(spoiler_file, "%s%s\n\n", INDENT1, art_ptr->misc_desc);
743 * @brief アーティファクト情報を出力するためにダミー生成を行う /
744 * Hack -- Create a "forged" artifact
745 * @param o_ptr 一時生成先を保管するオブジェクト構造体
746 * @param name1 生成するアーティファクトID
747 * @return 生成が成功した場合TRUEを返す
749 static bool make_fake_artifact(player_type *player_ptr, object_type *o_ptr, IDX name1)
751 artifact_type *a_ptr = &a_info[name1];
755 OBJECT_IDX i = lookup_kind(a_ptr->tval, a_ptr->sval);
759 object_prep(player_ptr, o_ptr, i);
760 o_ptr->name1 = (byte)name1;
761 o_ptr->pval = a_ptr->pval;
762 o_ptr->ac = a_ptr->ac;
763 o_ptr->dd = a_ptr->dd;
764 o_ptr->ds = a_ptr->ds;
765 o_ptr->to_a = a_ptr->to_a;
766 o_ptr->to_h = a_ptr->to_h;
767 o_ptr->to_d = a_ptr->to_d;
768 o_ptr->weight = a_ptr->weight;
773 * @brief アーティファクト情報のスポイラー出力を行うメインルーチン /
774 * Create a spoiler file for artifacts
775 * @param player_ptr プレーヤーへの参照ポインタ
776 * @param fname 生成ファイル名
779 static void spoil_artifact(player_type *player_ptr, concptr fname)
785 obj_desc_list artifact;
787 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
789 spoiler_file = angband_fopen(buf, "w");
792 msg_print("Cannot create spoiler file.");
797 for (i = 0; group_artifact[i].tval; i++) {
798 if (group_artifact[i].name) {
799 spoiler_blanklines(2);
800 spoiler_underline(group_artifact[i].name);
801 spoiler_blanklines(1);
804 for (j = 1; j < max_a_idx; ++j) {
805 artifact_type *a_ptr = &a_info[j];
806 if (a_ptr->tval != group_artifact[i].tval)
811 if (!make_fake_artifact(player_ptr, q_ptr, j))
814 object_analyze(player_ptr, q_ptr, &artifact);
815 spoiler_print_art(&artifact);
819 if (ferror(spoiler_file) || angband_fclose(spoiler_file)) {
820 msg_print("Cannot close spoiler file.");
824 msg_print("Successfully created a spoiler file.");
828 * @brief モンスター簡易情報のスポイラー出力を行うメインルーチン /
829 * Create a spoiler file for monsters -BEN-
830 * @param fname 生成ファイル名
833 static void spoil_mon_desc(player_type *player_ptr, concptr fname)
846 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
847 spoiler_file = angband_fopen(buf, "w");
849 msg_print("Cannot create spoiler file.");
853 C_MAKE(who, max_r_idx, MONRACE_IDX);
854 fprintf(spoiler_file, "Monster Spoilers for Hengband Version %d.%d.%d\n", FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
855 fprintf(spoiler_file, "------------------------------------------\n\n");
856 fprintf(spoiler_file, " %-38.38s%4s%4s%4s%7s%5s %11.11s\n", "Name", "Lev", "Rar", "Spd", "Hp", "Ac", "Visual Info");
857 fprintf(spoiler_file, "%-42.42s%4s%4s%4s%7s%5s %11.11s\n", "--------", "---", "---", "---", "--", "--", "-----------");
858 for (i = 1; i < max_r_idx; i++) {
859 monster_race *r_ptr = &r_info[i];
864 ang_sort(player_ptr, who, &why, n, ang_sort_comp_hook, ang_sort_swap_hook);
865 for (i = 0; i < n; i++) {
866 monster_race *r_ptr = &r_info[who[i]];
867 concptr name = (r_name + r_ptr->name);
868 if (r_ptr->flags7 & (RF7_KAGE))
870 else if (r_ptr->flags1 & (RF1_UNIQUE))
871 sprintf(nam, "[U] %s", name);
873 sprintf(nam, _(" %s", "The %s"), name);
875 sprintf(lev, "%d", (int)r_ptr->level);
876 sprintf(rar, "%d", (int)r_ptr->rarity);
877 if (r_ptr->speed >= 110)
878 sprintf(spd, "+%d", (r_ptr->speed - 110));
880 sprintf(spd, "-%d", (110 - r_ptr->speed));
882 sprintf(ac, "%d", r_ptr->ac);
883 if ((r_ptr->flags1 & (RF1_FORCE_MAXHP)) || (r_ptr->hside == 1))
884 sprintf(hp, "%d", r_ptr->hdice * r_ptr->hside);
886 sprintf(hp, "%dd%d", r_ptr->hdice, r_ptr->hside);
888 sprintf(exp, "%ld", (long)(r_ptr->mexp));
889 sprintf(exp, "%s '%c'", attr_to_text(r_ptr), r_ptr->d_char);
890 fprintf(spoiler_file, "%-42.42s%4s%4s%4s%7s%5s %11.11s\n", nam, lev, rar, spd, hp, ac, exp);
893 fprintf(spoiler_file, "\n");
894 C_KILL(who, max_r_idx, s16b);
895 if (ferror(spoiler_file) || angband_fclose(spoiler_file)) {
896 msg_print("Cannot close spoiler file.");
900 msg_print("Successfully created a spoiler file.");
904 * @brief 文字列をファイルポインタに出力する /
905 * Buffer text to the given file. (-SHAWN-)
906 * This is basically c_roff() from mon-desc.c with a few changes.
907 * @param str 文字列参照ポインタ
910 static void spoil_out(concptr str)
913 static char roff_buf[256];
914 static char roff_waiting_buf[256];
917 bool iskanji_flag = FALSE;
920 static char *roff_p = roff_buf;
921 static char *roff_s = NULL;
922 static bool waiting_output = FALSE;
924 if (waiting_output) {
925 fputs(roff_waiting_buf, spoiler_file);
926 waiting_output = FALSE;
929 if (roff_p != roff_buf)
931 while (*roff_p == ' ' && roff_p != roff_buf)
934 if (roff_p == roff_buf)
935 fprintf(spoiler_file, "\n");
937 *(roff_p + 1) = '\0';
938 fprintf(spoiler_file, "%s\n\n", roff_buf);
947 for (; *str; str++) {
950 bool k_flag = iskanji((unsigned char)(*str));
953 bool wrap = (ch == '\n');
956 if (!isprint((unsigned char)ch) && !k_flag && !iskanji_flag)
959 iskanji_flag = k_flag && !iskanji_flag;
965 if (waiting_output) {
966 fputs(roff_waiting_buf, spoiler_file);
968 fputc('\n', spoiler_file);
970 waiting_output = FALSE;
975 if (roff_p >= roff_buf + (k_flag ? 74 : 75))
977 else if ((ch == ' ') && (roff_p >= roff_buf + (k_flag ? 72 : 73)))
980 if (roff_p >= roff_buf + 75)
982 else if ((ch == ' ') && (roff_p >= roff_buf + 73))
989 bool iskanji_flag_local = FALSE;
990 concptr tail = str + (k_flag ? 2 : 1);
992 concptr tail = str + 1;
995 for (; *tail; tail++) {
1000 k_flag_local = iskanji((unsigned char)(*tail));
1001 if (isprint((unsigned char)*tail) || k_flag_local || iskanji_flag_local)
1004 iskanji_flag_local = k_flag_local && !iskanji_flag_local;
1012 waiting_output = TRUE;
1022 if (roff_s && (ch != ' ')) {
1030 if (!waiting_output)
1031 fprintf(spoiler_file, "%s\n", roff_buf);
1033 strcpy(roff_waiting_buf, roff_buf);
1045 if ((roff_p <= roff_buf) && (ch == ' '))
1050 if ((ch == ' ') || (ch == '('))
1053 if (iskanji_flag && strncmp(str, "。", 2) != 0 && strncmp(str, "、", 2) != 0 && strncmp(str, "ィ", 2) != 0 && strncmp(str, "ー", 2) != 0)
1066 * @brief 関数ポインタ用の出力関数 /
1067 * Hook function used in spoil_mon_info()
1069 * @param str 文字列参照ポインタ
1072 static void roff_func(TERM_COLOR attr, concptr str)
1079 * @brief モンスター詳細情報をスポイラー出力するメインルーチン /
1080 * Create a spoiler file for monsters (-SHAWN-)
1081 * @param fname ファイル名
1084 static void spoil_mon_info(player_type *player_ptr, concptr fname)
1091 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
1092 spoiler_file = angband_fopen(buf, "w");
1093 if (!spoiler_file) {
1094 msg_print("Cannot create spoiler file.");
1098 sprintf(buf, "Monster Spoilers for Hengband Version %d.%d.%d\n", FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
1100 spoil_out("------------------------------------------\n\n");
1101 C_MAKE(who, max_r_idx, MONRACE_IDX);
1102 for (i = 1; i < max_r_idx; i++) {
1103 monster_race *r_ptr = &r_info[i];
1108 ang_sort(player_ptr, who, &why, n, ang_sort_comp_hook, ang_sort_swap_hook);
1109 for (l = 0; l < n; l++) {
1110 monster_race *r_ptr = &r_info[who[l]];
1111 flags1 = r_ptr->flags1;
1112 if (flags1 & (RF1_UNIQUE)) {
1121 sprintf(buf, _("%s/%s (", "%s%s ("), (r_name + r_ptr->name), _(r_name + r_ptr->E_name, "")); /* ---)--- */
1123 spoil_out(attr_to_text(r_ptr));
1124 sprintf(buf, " '%c')\n", r_ptr->d_char);
1126 sprintf(buf, "=== ");
1128 sprintf(buf, "Num:%d ", who[l]);
1130 sprintf(buf, "Lev:%d ", (int)r_ptr->level);
1132 sprintf(buf, "Rar:%d ", r_ptr->rarity);
1134 if (r_ptr->speed >= 110)
1135 sprintf(buf, "Spd:+%d ", (r_ptr->speed - 110));
1137 sprintf(buf, "Spd:-%d ", (110 - r_ptr->speed));
1140 if ((flags1 & (RF1_FORCE_MAXHP)) || (r_ptr->hside == 1))
1141 sprintf(buf, "Hp:%d ", r_ptr->hdice * r_ptr->hside);
1143 sprintf(buf, "Hp:%dd%d ", r_ptr->hdice, r_ptr->hside);
1146 sprintf(buf, "Ac:%d ", r_ptr->ac);
1148 sprintf(buf, "Exp:%ld\n", (long)(r_ptr->mexp));
1150 output_monster_spoiler(player_ptr, who[l], roff_func);
1154 C_KILL(who, max_r_idx, s16b);
1155 if (ferror(spoiler_file) || angband_fclose(spoiler_file)) {
1156 msg_print("Cannot close spoiler file.");
1160 msg_print("Successfully created a spoiler file.");
1164 * @brief int配列でstrncmp()と似た比較処理を行う /
1165 * Compare two int-type array like strncmp() and return TRUE if equals
1166 * @param a 比較するint配列1
1167 * @param b 比較するint配列2
1169 * @return 両者の値が等しければTRUEを返す
1171 static bool int_n_cmp(int *a, int *b, int length)
1187 * @brief ある木が指定された木の部分木かどうかを返す /
1188 * Returns TRUE if an evolution tree is "partial tree"
1189 * @param tree 元となる木構造リスト
1190 * @param partial_tree 部分木かどうか判定したい木構造リスト
1191 * @return 部分木ならばTRUEを返す
1193 static bool is_partial_tree(int *tree, int *partial_tree)
1195 int pt_head = *(partial_tree++);
1197 while (partial_tree[pt_len])
1201 if (*(tree++) == pt_head) {
1202 if (int_n_cmp(tree, partial_tree, pt_len))
1211 * @brief 進化ツリーをスポイラー出力するメインルーチン /
1212 * Print monsters' evolution information to file
1213 * @param fname 出力ファイル名
1216 static void spoil_mon_evol(player_type *player_ptr, concptr fname)
1219 monster_race *r_ptr;
1220 int **evol_tree, i, j, n, r_idx;
1221 int *evol_tree_zero; /* For C_KILL() */
1222 path_build(buf, sizeof buf, ANGBAND_DIR_USER, fname);
1223 spoiler_file = angband_fopen(buf, "w");
1224 if (!spoiler_file) {
1225 msg_print("Cannot create spoiler file.");
1229 sprintf(buf, "Monster Spoilers for Hengband Version %d.%d.%d\n", FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
1231 spoil_out("------------------------------------------\n\n");
1232 C_MAKE(evol_tree, max_r_idx, int *);
1233 C_MAKE(*evol_tree, max_r_idx * (MAX_EVOL_DEPTH + 1), int);
1234 for (i = 1; i < max_r_idx; i++)
1235 evol_tree[i] = *evol_tree + i * (MAX_EVOL_DEPTH + 1);
1237 evol_tree_zero = *evol_tree;
1238 for (i = 1; i < max_r_idx; i++) {
1240 if (!r_ptr->next_exp)
1244 evol_tree[i][n++] = i;
1246 evol_tree[i][n++] = r_ptr->next_r_idx;
1247 r_ptr = &r_info[r_ptr->next_r_idx];
1248 } while (r_ptr->next_exp && (n < MAX_EVOL_DEPTH));
1251 for (i = 1; i < max_r_idx; i++) {
1252 if (!evol_tree[i][0])
1255 for (j = 1; j < max_r_idx; j++) {
1259 if (!evol_tree[j][0])
1262 if (is_partial_tree(evol_tree[j], evol_tree[i])) {
1263 evol_tree[i][0] = 0;
1269 ang_sort(player_ptr, evol_tree, NULL, max_r_idx, ang_sort_comp_evol_tree, ang_sort_swap_evol_tree);
1270 for (i = 0; i < max_r_idx; i++) {
1271 r_idx = evol_tree[i][0];
1275 r_ptr = &r_info[r_idx];
1276 fprintf(spoiler_file, _("[%d]: %s (レベル%d, '%c')\n", "[%d]: %s (Level %d, '%c')\n"), r_idx, r_name + r_ptr->name, (int)r_ptr->level, r_ptr->d_char);
1277 for (n = 1; r_ptr->next_exp; n++) {
1278 fprintf(spoiler_file, "%*s-(%ld)-> ", n * 2, "", (long int)r_ptr->next_exp);
1279 fprintf(spoiler_file, "[%d]: ", r_ptr->next_r_idx);
1280 r_ptr = &r_info[r_ptr->next_r_idx];
1281 fprintf(spoiler_file, _("%s (レベル%d, '%c')\n", "%s (Level %d, '%c')\n"), r_name + r_ptr->name, (int)r_ptr->level, r_ptr->d_char);
1284 fputc('\n', spoiler_file);
1287 C_KILL(evol_tree_zero, max_r_idx * (MAX_EVOL_DEPTH + 1), int);
1288 C_KILL(evol_tree, max_r_idx, int *);
1289 if (ferror(spoiler_file) || angband_fclose(spoiler_file)) {
1290 msg_print("Cannot close spoiler file.");
1294 msg_print("Successfully created a spoiler file.");
1298 * @brief スポイラー出力を行うコマンドのメインルーチン /
1299 * Create Spoiler files -BEN-
1302 void exe_output_spoilers(player_type *player_ptr)
1307 prt("Create a spoiler file.", 2, 0);
1308 prt("(1) Brief Object Info (obj-desc.txt)", 5, 5);
1309 prt("(2) Brief Artifact Info (artifact.txt)", 6, 5);
1310 prt("(3) Brief Monster Info (mon-desc.txt)", 7, 5);
1311 prt("(4) Full Monster Info (mon-info.txt)", 8, 5);
1312 prt("(5) Monster Evolution Info (mon-evol.txt)", 9, 5);
1313 prt(_("コマンド:", "Command: "), _(18, 12), 0);
1319 spoil_obj_desc(player_ptr, "obj-desc.txt");
1322 spoil_artifact(player_ptr, "artifact.txt");
1325 spoil_mon_desc(player_ptr, "mon-desc.txt");
1328 spoil_mon_info(player_ptr, "mon-info.txt");
1331 spoil_mon_evol(player_ptr, "mon-evol.txt");
1343 * @brief ランダムアーティファクト1件を解析する /
1344 * Fill in an object description structure for a given object
1345 * @param player_ptr プレーヤーへの参照ポインタ
1346 * @param o_ptr ランダムアーティファクトのオブジェクト構造体参照ポインタ
1347 * @param desc_ptr 記述内容を収める構造体参照ポインタ
1350 static void random_artifact_analyze(player_type *player_ptr, object_type *o_ptr, obj_desc_list *desc_ptr)
1352 analyze_general(player_ptr, o_ptr, desc_ptr->description);
1353 analyze_pval(player_ptr, o_ptr, &desc_ptr->pval_info);
1354 analyze_brand(player_ptr, o_ptr, desc_ptr->brands);
1355 analyze_slay(player_ptr, o_ptr, desc_ptr->slays);
1356 analyze_immune(player_ptr, o_ptr, desc_ptr->immunities);
1357 analyze_resist(player_ptr, o_ptr, desc_ptr->resistances);
1358 analyze_sustains(player_ptr, o_ptr, desc_ptr->sustains);
1359 analyze_misc_magic(player_ptr, o_ptr, desc_ptr->misc_magic);
1360 desc_ptr->activation = activation_explanation(player_ptr, o_ptr);
1361 sprintf(desc_ptr->misc_desc, _("重さ %d.%d kg", "Weight %d.%d lbs"), _(lbtokg1(o_ptr->weight), o_ptr->weight / 10),
1362 _(lbtokg2(o_ptr->weight), o_ptr->weight % 10));
1366 * @brief ランダムアーティファクト1件をスポイラー出力する /
1367 * Create a spoiler file entry for an artifact
1368 * @param o_ptr ランダムアーティファクトのオブジェクト構造体参照ポインタ
1369 * @param art_ptr 記述内容を収めた構造体参照ポインタ
1370 * Fill in an object description structure for a given object
1373 static void spoiler_print_randart(object_type *o_ptr, obj_desc_list *art_ptr)
1375 pval_info_type *pval_ptr = &art_ptr->pval_info;
1377 fprintf(spoiler_file, "%s\n", art_ptr->description);
1378 if (!object_is_fully_known(o_ptr)) {
1379 fprintf(spoiler_file, _("%s不明\n", "%sUnknown\n"), INDENT1);
1381 if (pval_ptr->pval_desc[0]) {
1382 sprintf(buf, _("%sの修正:", "%s to"), pval_ptr->pval_desc);
1383 spoiler_outlist(buf, pval_ptr->pval_affects, ITEM_SEP);
1386 spoiler_outlist(_("対:", "Slay"), art_ptr->slays, ITEM_SEP);
1387 spoiler_outlist(_("武器属性:", ""), art_ptr->brands, LIST_SEP);
1388 spoiler_outlist(_("免疫:", "Immunity to"), art_ptr->immunities, ITEM_SEP);
1389 spoiler_outlist(_("耐性:", "Resist"), art_ptr->resistances, ITEM_SEP);
1390 spoiler_outlist(_("維持:", "Sustain"), art_ptr->sustains, ITEM_SEP);
1391 spoiler_outlist("", art_ptr->misc_magic, LIST_SEP);
1392 if (art_ptr->activation) {
1393 fprintf(spoiler_file, _("%s発動: %s\n", "%sActivates for %s\n"), INDENT1, art_ptr->activation);
1397 fprintf(spoiler_file, "%s%s\n\n", INDENT1, art_ptr->misc_desc);
1401 * @brief ランダムアーティファクト内容をスポイラー出力するサブルーチン /
1402 * @param player_ptr プレーヤーへの参照ポインタ
1403 * @param o_ptr ランダムアーティファクトのオブジェクト構造体参照ポインタ
1404 * @param i 出力したい記録ランダムアーティファクトID
1407 static void spoil_random_artifact_aux(player_type *player_ptr, object_type *o_ptr, int i)
1409 obj_desc_list artifact;
1410 if (!object_is_known(o_ptr) || !o_ptr->art_name || o_ptr->tval != group_artifact[i].tval)
1413 random_artifact_analyze(player_ptr, o_ptr, &artifact);
1414 spoiler_print_randart(o_ptr, &artifact);
1418 * @brief ランダムアーティファクト内容をスポイラー出力するメインルーチン /
1419 * Create a list file for random artifacts
1420 * @param fname 出力ファイル名
1423 void spoil_random_artifact(player_type *creature_ptr, concptr fname)
1426 store_type *store_ptr;
1429 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
1430 spoiler_file = angband_fopen(buf, "w");
1431 if (!spoiler_file) {
1432 msg_print("Cannot create list file.");
1436 sprintf(buf, "Random artifacts list.\r");
1437 spoiler_underline(buf);
1438 for (j = 0; group_artifact[j].tval; j++) {
1439 for (i = INVEN_RARM; i < INVEN_TOTAL; i++) {
1440 q_ptr = &creature_ptr->inventory_list[i];
1441 spoil_random_artifact_aux(creature_ptr, q_ptr, j);
1444 for (i = 0; i < INVEN_PACK; i++) {
1445 q_ptr = &creature_ptr->inventory_list[i];
1446 spoil_random_artifact_aux(creature_ptr, q_ptr, j);
1449 store_ptr = &town_info[1].store[STORE_HOME];
1450 for (i = 0; i < store_ptr->stock_num; i++) {
1451 q_ptr = &store_ptr->stock[i];
1452 spoil_random_artifact_aux(creature_ptr, q_ptr, j);
1455 store_ptr = &town_info[1].store[STORE_MUSEUM];
1456 for (i = 0; i < store_ptr->stock_num; i++) {
1457 q_ptr = &store_ptr->stock[i];
1458 spoil_random_artifact_aux(creature_ptr, q_ptr, j);
1462 if (ferror(spoiler_file) || angband_fclose(spoiler_file)) {
1463 msg_print("Cannot close list file.");
1467 msg_print("Successfully created a list file.");