OSDN Git Service

[Refcator] #40512 Reshaped wizard-special-process.c
[hengbandforosx/hengbandosx.git] / src / wizard / wizard-spoiler.c
1 /*!
2  * @brief ウィザードモードの処理(スポイラー出力中心) / Spoiler generation -BEN-
3  * @date 2014/02/17
4  * @author
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.
10  */
11
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"
49
50 /* ITEM_SEP separates items within a list */
51 #define ITEM_SEP ','
52
53 /* LIST_SEP separates lists */
54 #define LIST_SEP _(',', ';')
55
56 #define MAX_EVOL_DEPTH 64
57
58 /*
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.)
62  */
63 #define INDENT1 "    "
64 #define INDENT2 "      "
65
66 /* MAX_LINE_LEN specifies when a line should wrap. */
67 #define MAX_LINE_LEN 75
68
69 /* Returns a "+" string if a number is non-negative and an empty string if negative */
70 #define POSITIZE(v) (((v) >= 0) ? "+" : "")
71
72 /* Given an array, determine how many elements are in the array */
73 #define N_ELEMENTS(a) (sizeof(a) / sizeof((a)[0]))
74
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. */
78
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];
81 } pval_info_type;
82
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 */
91
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 */
94         + 1 /* TY curse */
95         + 1 /* type of curse */
96         + 1]; /* sentinel NULL */
97
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" */
101 } obj_desc_list;
102
103 /*
104  * The spoiler file being created
105  */
106 static FILE *spoiler_file = NULL;
107
108 /*!
109  * @brief シンボル職の記述名を返す /
110  * Extract a textual representation of an attribute
111  * @param r_ptr モンスター種族の構造体ポインタ
112  * @return シンボル職の記述名
113  */
114 static concptr attr_to_text(monster_race *r_ptr)
115 {
116     if (r_ptr->flags1 & RF1_ATTR_CLEAR)
117         return _("透明な", "Clear");
118
119     if (r_ptr->flags1 & RF1_ATTR_MULTI)
120         return _("万色の", "Multi");
121
122     if (r_ptr->flags1 & RF1_ATTR_SEMIRAND)
123         return _("準ランダムな", "S.Rand");
124
125     switch (r_ptr->d_attr) {
126     case TERM_DARK:
127         return _("黒い", "Dark");
128     case TERM_WHITE:
129         return _("白い", "White");
130     case TERM_SLATE:
131         return _("青灰色の", "Slate");
132     case TERM_ORANGE:
133         return _("オレンジの", "Orange");
134     case TERM_RED:
135         return _("赤い", "Red");
136     case TERM_GREEN:
137         return _("緑の", "Green");
138     case TERM_BLUE:
139         return _("青い", "Blue");
140     case TERM_UMBER:
141         return _("琥珀色の", "Umber");
142     case TERM_L_DARK:
143         return _("灰色の", "L.Dark");
144     case TERM_L_WHITE:
145         return _("明るい青灰色の", "L.Slate");
146     case TERM_VIOLET:
147         return _("紫の", "Violet");
148     case TERM_YELLOW:
149         return _("黄色の", "Yellow");
150     case TERM_L_RED:
151         return _("明るい赤の", "L.Red");
152     case TERM_L_GREEN:
153         return _("明るい緑の", "L.Green");
154     case TERM_L_BLUE:
155         return _("明るい青の", "L.Blue");
156     case TERM_L_UMBER:
157         return _("明るい琥珀色の", "L.Umber");
158     }
159
160     return _("変な色の", "Icky");
161 }
162
163 /*!
164  * @brief ベースアイテムの各情報を文字列化する /
165  * Describe the kind
166  * @param player_ptr プレーヤーへの参照ポインタ
167  * @param buf 名称を返すバッファ参照ポインタ
168  * @param dam ダメージダイス記述を返すバッファ参照ポインタ
169  * @param wgt 重量記述を返すバッファ参照ポインタ
170  * @param lev 生成階記述を返すバッファ参照ポインタ
171  * @param chance 生成機会を返すバッファ参照ポインタ
172  * @param val 価値を返すバッファ参照ポインタ
173  * @param k ベースアイテムID
174  * @return なし
175  */
176 static void kind_info(player_type *player_ptr, char *buf, char *dam, char *wgt, char *chance, DEPTH *lev, PRICE *val, OBJECT_IDX k)
177 {
178     object_type forge;
179     object_type *q_ptr;
180     int i;
181     q_ptr = &forge;
182
183     object_prep(player_ptr, q_ptr, k);
184     q_ptr->ident |= (IDENT_KNOWN);
185     q_ptr->pval = 0;
186     q_ptr->to_a = 0;
187     q_ptr->to_h = 0;
188     q_ptr->to_d = 0;
189
190     (*lev) = k_info[q_ptr->k_idx].level;
191     (*val) = object_value(player_ptr, q_ptr);
192     if (!buf || !dam || !chance || !wgt)
193         return;
194
195     describe_flavor(player_ptr, buf, q_ptr, (OD_NAME_ONLY | OD_STORE));
196     strcpy(dam, "");
197     switch (q_ptr->tval) {
198     case TV_BOW: {
199         break;
200     }
201     case TV_SHOT:
202     case TV_BOLT:
203     case TV_ARROW: {
204         sprintf(dam, "%dd%d", q_ptr->dd, q_ptr->ds);
205         break;
206     }
207     case TV_HAFTED:
208     case TV_POLEARM:
209     case TV_SWORD:
210     case TV_DIGGING: {
211         sprintf(dam, "%dd%d", q_ptr->dd, q_ptr->ds);
212         break;
213     }
214     case TV_BOOTS:
215     case TV_GLOVES:
216     case TV_CLOAK:
217     case TV_CROWN:
218     case TV_HELM:
219     case TV_SHIELD:
220     case TV_SOFT_ARMOR:
221     case TV_HARD_ARMOR:
222     case TV_DRAG_ARMOR: {
223         sprintf(dam, "%d", q_ptr->ac);
224         break;
225     }
226     }
227
228     strcpy(chance, "");
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);
234         }
235     }
236
237     sprintf(wgt, "%3d.%d", (int)(q_ptr->weight / 10), (int)(q_ptr->weight % 10));
238 }
239
240 /*!
241  * @brief 各ベースアイテムの情報を一行毎に記述する /
242  * @param player_ptr プレーヤーへの参照ポインタ
243  * Create a spoiler file for items
244  * @param fname ファイル名
245  * @return なし
246  */
247 static void spoil_obj_desc(player_type *player_ptr, concptr fname)
248 {
249     int i, k, s, t, n = 0, group_start = 0;
250     OBJECT_IDX who[200];
251     char buf[1024];
252     char wgt[80];
253     char chance[80];
254     char dam[80];
255
256     path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
257     spoiler_file = angband_fopen(buf, "w");
258
259     if (!spoiler_file) {
260         msg_print("Cannot create spoiler file.");
261         return;
262     }
263
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) {
269             if (n) {
270                 for (s = 0; s < n - 1; s++) {
271                     for (t = 0; t < n - 1; t++) {
272                         int i1 = t;
273                         int i2 = t + 1;
274
275                         DEPTH e1;
276                         DEPTH e2;
277
278                         PRICE t1;
279                         PRICE t2;
280
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]);
283
284                         if ((t1 > t2) || ((t1 == t2) && (e1 > e2))) {
285                             u16b tmp = who[i1];
286                             who[i1] = who[i2];
287                             who[i2] = tmp;
288                         }
289                     }
290                 }
291
292                 fprintf(spoiler_file, "\n\n%s\n\n", group_item[group_start].name);
293                 for (s = 0; s < n; s++) {
294                     DEPTH e;
295                     PRICE v;
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));
298                 }
299
300                 n = 0;
301             }
302
303             if (!group_item[i].tval)
304                 break;
305
306             group_start = i;
307         }
308
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))
312                 continue;
313
314             who[n++] = (u16b)k;
315         }
316     }
317
318     if (ferror(spoiler_file) || angband_fclose(spoiler_file)) {
319         msg_print("Cannot close spoiler file.");
320         return;
321     }
322
323     msg_print("Successfully created a spoiler file.");
324 }
325
326 /*!
327  * @brief ファイルポインタ先に同じ文字を複数出力する /
328  * Write out `n' of the character `c' to the spoiler file
329  * @param n 出力する数
330  * @param c 出力するキャラクタ
331  * @return なし
332  */
333 static void spoiler_out_n_chars(int n, char c)
334 {
335     while (--n >= 0)
336         fputc(c, spoiler_file);
337 }
338
339 /*!
340  * @brief ファイルポインタ先に改行を複数出力する /
341  * Write out `n' blank lines to the spoiler file
342  * @param n 改行を出力する数
343  * @return なし
344  */
345 static void spoiler_blanklines(int n) { spoiler_out_n_chars(n, '\n'); }
346
347 /*!
348  * @brief ファイルポインタ先に複数のハイフンで装飾した文字列を出力する /
349  * Write a line to the spoiler file and then "underline" it with hypens
350  * @param str 出力したい文字列
351  * @return なし
352  */
353 static void spoiler_underline(concptr str)
354 {
355     fprintf(spoiler_file, "%s\n", str);
356     spoiler_out_n_chars(strlen(str), '-');
357     fprintf(spoiler_file, "\n");
358 }
359
360 /*!
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と同じアドレス
368  * @details
369  * <pre>
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.
377  * </pre>
378  */
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)
380 {
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;
384         }
385     }
386
387     return desc_ptr;
388 }
389
390 /*!
391  * @brief アイテムの特定記述内容を返す /
392  * Acquire a "basic" description "The Cloak of Death [1,+10]"
393  * @param o_ptr 記述を得たいオブジェクトの参照ポインタ
394  * @param desc_ptr 記述内容を返すための文字列参照ポインタ
395  * @return なし
396  */
397 static void analyze_general(player_type *player_ptr, object_type *o_ptr, char *desc_ptr)
398 {
399     describe_flavor(player_ptr, desc_ptr, o_ptr, (OD_NAME_AND_ENCHANT | OD_STORE));
400 }
401
402 /*!
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修正構造体の参照ポインタ
408  * @return なし
409  */
410 static void analyze_pval(player_type *player_ptr, object_type *o_ptr, pval_info_type *pi_ptr)
411 {
412     BIT_FLAGS flgs[TR_FLAG_SIZE];
413     concptr *affects_list;
414     if (!o_ptr->pval) {
415         pi_ptr->pval_desc[0] = '\0';
416         return;
417     }
418
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));
428     }
429
430     affects_list = spoiler_flag_aux(flgs, pval_flags1_desc, affects_list, N_ELEMENTS(pval_flags1_desc));
431     *affects_list = NULL;
432 }
433
434 /*!
435  * @brief アーティファクトの種族スレイ特性を構造体に収める /
436  * Note the slaying specialties of a weapon
437  * @param o_ptr オブジェクト構造体の参照ポインタ
438  * @param slay_list 種族スレイ構造体の参照ポインタ
439  * @return なし
440  */
441 static void analyze_slay(player_type *player_ptr, object_type *o_ptr, concptr *slay_list)
442 {
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));
446     *slay_list = NULL;
447 }
448
449 /*!
450  * @brief アーティファクトの属性ブランド特性を構造体に収める /
451  * Note an object's elemental brands
452  * @param o_ptr オブジェクト構造体の参照ポインタ
453  * @param brand_list 属性ブランド構造体の参照ポインタ
454  * @return なし
455  */
456 static void analyze_brand(player_type *player_ptr, object_type *o_ptr, concptr *brand_list)
457 {
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));
461     *brand_list = NULL;
462 }
463
464 /*!
465  * @brief アーティファクトの通常耐性を構造体に収める /
466  * Note an object's elemental brands
467  * @param o_ptr オブジェクト構造体の参照ポインタ
468  * @param resist_list 通常耐性構造体の参照ポインタ
469  * @return なし
470  */
471 static void analyze_resist(player_type *player_ptr, object_type *o_ptr, concptr *resist_list)
472 {
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));
476     *resist_list = NULL;
477 }
478
479 /*!
480  * @brief アーティファクトの免疫特性を構造体に収める /
481  * Note the immunities granted by an object
482  * @param o_ptr オブジェクト構造体の参照ポインタ
483  * @param immune_list 免疫構造体の参照ポインタ
484  * @return なし
485  */
486 static void analyze_immune(player_type *player_ptr, object_type *o_ptr, concptr *immune_list)
487 {
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));
491     *immune_list = NULL;
492 }
493
494 /*!
495  * @brief アーティファクトの維持特性を構造体に収める /
496  * Note which stats an object sustains
497  * @param o_ptr オブジェクト構造体の参照ポインタ
498  * @param sustain_list 維持特性構造体の参照ポインタ
499  * @return なし
500  */
501 static void analyze_sustains(player_type *player_ptr, object_type *o_ptr, concptr *sustain_list)
502 {
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));
511     }
512
513     *sustain_list = NULL;
514 }
515
516 /*!
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 その他の特性構造体の参照ポインタ
522  * @return なし
523  */
524 static void analyze_misc_magic(player_type *player_ptr, object_type *o_ptr, concptr *misc_list)
525 {
526     BIT_FLAGS flgs[TR_FLAG_SIZE];
527     char desc[256];
528
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));
532     POSITION rad = 0;
533     if (have_flag(flgs, TR_LITE_1))
534         rad += 1;
535     if (have_flag(flgs, TR_LITE_2))
536         rad += 2;
537     if (have_flag(flgs, TR_LITE_3))
538         rad += 3;
539     if (have_flag(flgs, TR_LITE_M1))
540         rad -= 1;
541     if (have_flag(flgs, TR_LITE_M2))
542         rad -= 2;
543     if (have_flag(flgs, TR_LITE_M3))
544         rad -= 3;
545
546     if (o_ptr->name2 == EGO_LITE_SHINE)
547         rad++;
548
549     if (have_flag(flgs, TR_LITE_FUEL)) {
550         if (rad > 0)
551             sprintf(desc, _("それは燃料補給によって明かり(半径 %d)を授ける。", "It provides light (radius %d) when fueled."), (int)rad);
552     } else {
553         if (rad > 0)
554             sprintf(desc, _("永久光源(半径 %d)", "Permanent Light(radius %d)"), (int)rad);
555         if (rad < 0)
556             sprintf(desc, _("永久光源(半径-%d)。", "Permanent Light(radius -%d)"), (int)-rad);
557     }
558
559     if (rad != 0)
560         *misc_list++ = quark_str(quark_add(desc));
561
562     if (have_flag(flgs, TR_TY_CURSE))
563         *misc_list++ = _("太古の怨念", "Ancient Curse");
564
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");
571
572     if (have_flag(flgs, TR_ADD_L_CURSE))
573         *misc_list++ = _("呪いを増やす", "Cursing");
574
575     if (have_flag(flgs, TR_ADD_H_CURSE))
576         *misc_list++ = _("強力な呪いを増やす", "Heavily Cursing");
577
578     *misc_list = NULL;
579 }
580
581 /*!
582  * @brief アーティファクトの追加ランダム特性を構造体に収める /
583  * Note additional ability and/or resistance of fixed artifacts
584  * @param o_ptr オブジェクト構造体の参照ポインタ
585  * @param addition 追加ランダム耐性構造体の参照ポインタ
586  * @return なし
587  */
588 static void analyze_addition(object_type *o_ptr, char *addition)
589 {
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"));
604 }
605
606 /*!
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 基本情報を収める文字列参照ポインタ
612  * @return なし
613  */
614 static void analyze_misc(object_type *o_ptr, char *misc_desc)
615 {
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);
619 }
620
621 /*!
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 全アーティファクト情報を収める文字列参照ポインタ
628  * @return なし
629  */
630 static void object_analyze(player_type *player_ptr, object_type *o_ptr, obj_desc_list *desc_ptr)
631 {
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);
643 }
644
645 /*!
646  * @brief バッファにアーティファクト出力情報ヘッダを収める /
647  * @return なし
648  */
649 static void print_header(void)
650 {
651     char buf[80];
652
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);
655 }
656
657 /*!
658  * @brief フラグ名称を出力する汎用関数
659  * @param header ヘッダに出力するフラグ群の名前
660  * @param list フラグ名リスト
661  * @param separator フラグ表示の区切り記号
662  * @return なし
663  */
664 static void spoiler_outlist(concptr header, concptr *list, char separator)
665 {
666     int buf_len;
667     char line[MAX_LINE_LEN + 1], buf[80];
668
669     if (*list == NULL)
670         return;
671
672     strcpy(line, INDENT1);
673     if (header && (header[0])) {
674         strcat(line, header);
675         strcat(line, " ");
676     }
677
678     int line_len = strlen(line);
679     while (TRUE) {
680         strcpy(buf, *list);
681         buf_len = strlen(buf);
682         if (list[1]) {
683             sprintf(buf + buf_len, "%c ", separator);
684             buf_len += 2;
685         }
686
687         if (line_len + buf_len <= MAX_LINE_LEN) {
688             strcat(line, buf);
689             line_len += buf_len;
690         } else {
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);
695             } else {
696                 fprintf(spoiler_file, "%s\n", line);
697                 sprintf(line, "%s%s", INDENT2, buf);
698             }
699
700             line_len = strlen(line);
701         }
702
703         if (!*++list)
704             break;
705     }
706
707     fprintf(spoiler_file, "%s\n", line);
708 }
709
710 /*!
711  * @brief アーティファクト一件をスポイラー出力する /
712  * Create a spoiler file entry for an artifact
713  * @param art_ptr アーティファクト情報をまとめた構造体の参照ポインタ
714  * @return なし
715  */
716 static void spoiler_print_art(obj_desc_list *art_ptr)
717 {
718     pval_info_type *pval_ptr = &art_ptr->pval_info;
719     char buf[80];
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);
724     }
725
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);
732
733     if (art_ptr->addition[0])
734         fprintf(spoiler_file, _("%s追加: %s\n", "%sAdditional %s\n"), INDENT1, art_ptr->addition);
735
736     if (art_ptr->activation)
737         fprintf(spoiler_file, _("%s発動: %s\n", "%sActivates for %s\n"), INDENT1, art_ptr->activation);
738
739     fprintf(spoiler_file, "%s%s\n\n", INDENT1, art_ptr->misc_desc);
740 }
741
742 /*!
743  * @brief アーティファクト情報を出力するためにダミー生成を行う /
744  * Hack -- Create a "forged" artifact
745  * @param o_ptr 一時生成先を保管するオブジェクト構造体
746  * @param name1 生成するアーティファクトID
747  * @return 生成が成功した場合TRUEを返す
748  */
749 static bool make_fake_artifact(player_type *player_ptr, object_type *o_ptr, IDX name1)
750 {
751     artifact_type *a_ptr = &a_info[name1];
752     if (!a_ptr->name)
753         return FALSE;
754
755     OBJECT_IDX i = lookup_kind(a_ptr->tval, a_ptr->sval);
756     if (!i)
757         return FALSE;
758
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;
769     return TRUE;
770 }
771
772 /*!
773  * @brief アーティファクト情報のスポイラー出力を行うメインルーチン /
774  * Create a spoiler file for artifacts
775  * @param player_ptr プレーヤーへの参照ポインタ
776  * @param fname 生成ファイル名
777  * @return なし
778  */
779 static void spoil_artifact(player_type *player_ptr, concptr fname)
780 {
781     int i;
782     IDX j;
783     object_type forge;
784     object_type *q_ptr;
785     obj_desc_list artifact;
786     char buf[1024];
787     path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
788
789     spoiler_file = angband_fopen(buf, "w");
790
791     if (!spoiler_file) {
792         msg_print("Cannot create spoiler file.");
793         return;
794     }
795
796     print_header();
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);
802         }
803
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)
807                 continue;
808
809             q_ptr = &forge;
810             object_wipe(q_ptr);
811             if (!make_fake_artifact(player_ptr, q_ptr, j))
812                 continue;
813
814             object_analyze(player_ptr, q_ptr, &artifact);
815             spoiler_print_art(&artifact);
816         }
817     }
818
819     if (ferror(spoiler_file) || angband_fclose(spoiler_file)) {
820         msg_print("Cannot close spoiler file.");
821         return;
822     }
823
824     msg_print("Successfully created a spoiler file.");
825 }
826
827 /*!
828  * @brief モンスター簡易情報のスポイラー出力を行うメインルーチン /
829  * Create a spoiler file for monsters   -BEN-
830  * @param fname 生成ファイル名
831  * @return なし
832  */
833 static void spoil_mon_desc(player_type *player_ptr, concptr fname)
834 {
835     int i, n = 0;
836     u16b why = 2;
837     MONRACE_IDX *who;
838     char buf[1024];
839     char nam[80];
840     char lev[80];
841     char rar[80];
842     char spd[80];
843     char ac[80];
844     char hp[80];
845     char exp[80];
846     path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
847     spoiler_file = angband_fopen(buf, "w");
848     if (!spoiler_file) {
849         msg_print("Cannot create spoiler file.");
850         return;
851     }
852
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];
860         if (r_ptr->name)
861             who[n++] = (s16b)i;
862     }
863
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))
869             continue;
870         else if (r_ptr->flags1 & (RF1_UNIQUE))
871             sprintf(nam, "[U] %s", name);
872         else
873             sprintf(nam, _("    %s", "The %s"), name);
874
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));
879         else
880             sprintf(spd, "-%d", (110 - r_ptr->speed));
881
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);
885         else
886             sprintf(hp, "%dd%d", r_ptr->hdice, r_ptr->hside);
887
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);
891     }
892
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.");
897         return;
898     }
899
900     msg_print("Successfully created a spoiler file.");
901 }
902
903 /*!
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 文字列参照ポインタ
908  * @return なし
909  */
910 static void spoil_out(concptr str)
911 {
912     concptr r;
913     static char roff_buf[256];
914     static char roff_waiting_buf[256];
915
916 #ifdef JP
917     bool iskanji_flag = FALSE;
918 #endif
919
920     static char *roff_p = roff_buf;
921     static char *roff_s = NULL;
922     static bool waiting_output = FALSE;
923     if (!str) {
924         if (waiting_output) {
925             fputs(roff_waiting_buf, spoiler_file);
926             waiting_output = FALSE;
927         }
928
929         if (roff_p != roff_buf)
930             roff_p--;
931         while (*roff_p == ' ' && roff_p != roff_buf)
932             roff_p--;
933
934         if (roff_p == roff_buf)
935             fprintf(spoiler_file, "\n");
936         else {
937             *(roff_p + 1) = '\0';
938             fprintf(spoiler_file, "%s\n\n", roff_buf);
939         }
940
941         roff_p = roff_buf;
942         roff_s = NULL;
943         roff_buf[0] = '\0';
944         return;
945     }
946
947     for (; *str; str++) {
948 #ifdef JP
949         char cbak;
950         bool k_flag = iskanji((unsigned char)(*str));
951 #endif
952         char ch = *str;
953         bool wrap = (ch == '\n');
954
955 #ifdef JP
956         if (!isprint((unsigned char)ch) && !k_flag && !iskanji_flag)
957             ch = ' ';
958
959         iskanji_flag = k_flag && !iskanji_flag;
960 #else
961         if (!isprint(ch))
962             ch = ' ';
963 #endif
964
965         if (waiting_output) {
966             fputs(roff_waiting_buf, spoiler_file);
967             if (!wrap)
968                 fputc('\n', spoiler_file);
969
970             waiting_output = FALSE;
971         }
972
973         if (!wrap) {
974 #ifdef JP
975             if (roff_p >= roff_buf + (k_flag ? 74 : 75))
976                 wrap = TRUE;
977             else if ((ch == ' ') && (roff_p >= roff_buf + (k_flag ? 72 : 73)))
978                 wrap = TRUE;
979 #else
980             if (roff_p >= roff_buf + 75)
981                 wrap = TRUE;
982             else if ((ch == ' ') && (roff_p >= roff_buf + 73))
983                 wrap = TRUE;
984 #endif
985
986             if (wrap) {
987 #ifdef JP
988                 bool k_flag_local;
989                 bool iskanji_flag_local = FALSE;
990                 concptr tail = str + (k_flag ? 2 : 1);
991 #else
992                 concptr tail = str + 1;
993 #endif
994
995                 for (; *tail; tail++) {
996                     if (*tail == ' ')
997                         continue;
998
999 #ifdef JP
1000                     k_flag_local = iskanji((unsigned char)(*tail));
1001                     if (isprint((unsigned char)*tail) || k_flag_local || iskanji_flag_local)
1002                         break;
1003
1004                     iskanji_flag_local = k_flag_local && !iskanji_flag_local;
1005 #else
1006                     if (isprint(*tail))
1007                         break;
1008 #endif
1009                 }
1010
1011                 if (!*tail)
1012                     waiting_output = TRUE;
1013             }
1014         }
1015
1016         if (wrap) {
1017             *roff_p = '\0';
1018             r = roff_p;
1019 #ifdef JP
1020             cbak = ' ';
1021 #endif
1022             if (roff_s && (ch != ' ')) {
1023 #ifdef JP
1024                 cbak = *roff_s;
1025 #endif
1026                 *roff_s = '\0';
1027                 r = roff_s + 1;
1028             }
1029
1030             if (!waiting_output)
1031                 fprintf(spoiler_file, "%s\n", roff_buf);
1032             else
1033                 strcpy(roff_waiting_buf, roff_buf);
1034
1035             roff_s = NULL;
1036             roff_p = roff_buf;
1037 #ifdef JP
1038             if (cbak != ' ')
1039                 *roff_p++ = cbak;
1040 #endif
1041             while (*r)
1042                 *roff_p++ = *r++;
1043         }
1044
1045         if ((roff_p <= roff_buf) && (ch == ' '))
1046             continue;
1047
1048 #ifdef JP
1049         if (!k_flag) {
1050             if ((ch == ' ') || (ch == '('))
1051                 roff_s = roff_p;
1052         } else {
1053             if (iskanji_flag && strncmp(str, "。", 2) != 0 && strncmp(str, "、", 2) != 0 && strncmp(str, "ィ", 2) != 0 && strncmp(str, "ー", 2) != 0)
1054                 roff_s = roff_p;
1055         }
1056 #else
1057         if (ch == ' ')
1058             roff_s = roff_p;
1059 #endif
1060
1061         *roff_p++ = ch;
1062     }
1063 }
1064
1065 /*!
1066  * @brief 関数ポインタ用の出力関数 /
1067  * Hook function used in spoil_mon_info()
1068  * @param attr 未使用
1069  * @param str 文字列参照ポインタ
1070  * @return なし
1071  */
1072 static void roff_func(TERM_COLOR attr, concptr str)
1073 {
1074     (void)attr;
1075     spoil_out(str);
1076 }
1077
1078 /*!
1079  * @brief モンスター詳細情報をスポイラー出力するメインルーチン /
1080  * Create a spoiler file for monsters (-SHAWN-)
1081  * @param fname ファイル名
1082  * @return なし
1083  */
1084 static void spoil_mon_info(player_type *player_ptr, concptr fname)
1085 {
1086     char buf[1024];
1087     int i, l, n = 0;
1088     BIT_FLAGS flags1;
1089     u16b why = 2;
1090     MONRACE_IDX *who;
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.");
1095         return;
1096     }
1097
1098     sprintf(buf, "Monster Spoilers for Hengband Version %d.%d.%d\n", FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
1099     spoil_out(buf);
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];
1104         if (r_ptr->name)
1105             who[n++] = (s16b)i;
1106     }
1107
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)) {
1113             spoil_out("[U] ");
1114         } else {
1115 #ifdef JP
1116 #else
1117             spoil_out("The ");
1118 #endif
1119         }
1120
1121         sprintf(buf, _("%s/%s  (", "%s%s ("), (r_name + r_ptr->name), _(r_name + r_ptr->E_name, "")); /* ---)--- */
1122         spoil_out(buf);
1123         spoil_out(attr_to_text(r_ptr));
1124         sprintf(buf, " '%c')\n", r_ptr->d_char);
1125         spoil_out(buf);
1126         sprintf(buf, "=== ");
1127         spoil_out(buf);
1128         sprintf(buf, "Num:%d  ", who[l]);
1129         spoil_out(buf);
1130         sprintf(buf, "Lev:%d  ", (int)r_ptr->level);
1131         spoil_out(buf);
1132         sprintf(buf, "Rar:%d  ", r_ptr->rarity);
1133         spoil_out(buf);
1134         if (r_ptr->speed >= 110)
1135             sprintf(buf, "Spd:+%d  ", (r_ptr->speed - 110));
1136         else
1137             sprintf(buf, "Spd:-%d  ", (110 - r_ptr->speed));
1138
1139         spoil_out(buf);
1140         if ((flags1 & (RF1_FORCE_MAXHP)) || (r_ptr->hside == 1))
1141             sprintf(buf, "Hp:%d  ", r_ptr->hdice * r_ptr->hside);
1142         else
1143             sprintf(buf, "Hp:%dd%d  ", r_ptr->hdice, r_ptr->hside);
1144
1145         spoil_out(buf);
1146         sprintf(buf, "Ac:%d  ", r_ptr->ac);
1147         spoil_out(buf);
1148         sprintf(buf, "Exp:%ld\n", (long)(r_ptr->mexp));
1149         spoil_out(buf);
1150         output_monster_spoiler(player_ptr, who[l], roff_func);
1151         spoil_out(NULL);
1152     }
1153
1154     C_KILL(who, max_r_idx, s16b);
1155     if (ferror(spoiler_file) || angband_fclose(spoiler_file)) {
1156         msg_print("Cannot close spoiler file.");
1157         return;
1158     }
1159
1160     msg_print("Successfully created a spoiler file.");
1161 }
1162
1163 /*!
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
1168  * @param length
1169  * @return 両者の値が等しければTRUEを返す
1170  */
1171 static bool int_n_cmp(int *a, int *b, int length)
1172 {
1173     if (!length)
1174         return TRUE;
1175
1176     do {
1177         if (*a != *(b++))
1178             return FALSE;
1179         if (!(*(a++)))
1180             break;
1181     } while (--length);
1182
1183     return TRUE;
1184 }
1185
1186 /*!
1187  * @brief ある木が指定された木の部分木かどうかを返す /
1188  * Returns TRUE if an evolution tree is "partial tree"
1189  * @param tree 元となる木構造リスト
1190  * @param partial_tree 部分木かどうか判定したい木構造リスト
1191  * @return 部分木ならばTRUEを返す
1192  */
1193 static bool is_partial_tree(int *tree, int *partial_tree)
1194 {
1195     int pt_head = *(partial_tree++);
1196     int pt_len = 0;
1197     while (partial_tree[pt_len])
1198         pt_len++;
1199
1200     while (*tree) {
1201         if (*(tree++) == pt_head) {
1202             if (int_n_cmp(tree, partial_tree, pt_len))
1203                 return TRUE;
1204         }
1205     }
1206
1207     return FALSE;
1208 }
1209
1210 /*!
1211  * @brief 進化ツリーをスポイラー出力するメインルーチン /
1212  * Print monsters' evolution information to file
1213  * @param fname 出力ファイル名
1214  * @return なし
1215  */
1216 static void spoil_mon_evol(player_type *player_ptr, concptr fname)
1217 {
1218     char buf[1024];
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.");
1226         return;
1227     }
1228
1229     sprintf(buf, "Monster Spoilers for Hengband Version %d.%d.%d\n", FAKE_VER_MAJOR - 10, FAKE_VER_MINOR, FAKE_VER_PATCH);
1230     spoil_out(buf);
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);
1236
1237     evol_tree_zero = *evol_tree;
1238     for (i = 1; i < max_r_idx; i++) {
1239         r_ptr = &r_info[i];
1240         if (!r_ptr->next_exp)
1241             continue;
1242
1243         n = 0;
1244         evol_tree[i][n++] = i;
1245         do {
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));
1249     }
1250
1251     for (i = 1; i < max_r_idx; i++) {
1252         if (!evol_tree[i][0])
1253             continue;
1254
1255         for (j = 1; j < max_r_idx; j++) {
1256             if (i == j)
1257                 continue;
1258
1259             if (!evol_tree[j][0])
1260                 continue;
1261
1262             if (is_partial_tree(evol_tree[j], evol_tree[i])) {
1263                 evol_tree[i][0] = 0;
1264                 break;
1265             }
1266         }
1267     }
1268
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];
1272         if (!r_idx)
1273             continue;
1274
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);
1282         }
1283
1284         fputc('\n', spoiler_file);
1285     }
1286
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.");
1291         return;
1292     }
1293
1294     msg_print("Successfully created a spoiler file.");
1295 }
1296
1297 /*!
1298  * @brief スポイラー出力を行うコマンドのメインルーチン /
1299  * Create Spoiler files -BEN-
1300  * @return なし
1301  */
1302 void exe_output_spoilers(player_type *player_ptr)
1303 {
1304     screen_save();
1305     while (TRUE) {
1306         term_clear();
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);
1314         switch (inkey()) {
1315         case ESCAPE:
1316             screen_load();
1317             return;
1318         case '1':
1319             spoil_obj_desc(player_ptr, "obj-desc.txt");
1320             break;
1321         case '2':
1322             spoil_artifact(player_ptr, "artifact.txt");
1323             break;
1324         case '3':
1325             spoil_mon_desc(player_ptr, "mon-desc.txt");
1326             break;
1327         case '4':
1328             spoil_mon_info(player_ptr, "mon-info.txt");
1329             break;
1330         case '5':
1331             spoil_mon_evol(player_ptr, "mon-evol.txt");
1332             break;
1333         default:
1334             bell();
1335             break;
1336         }
1337
1338         msg_erase();
1339     }
1340 }
1341
1342 /*!
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 記述内容を収める構造体参照ポインタ
1348  * @return なし
1349  */
1350 static void random_artifact_analyze(player_type *player_ptr, object_type *o_ptr, obj_desc_list *desc_ptr)
1351 {
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));
1363 }
1364
1365 /*!
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
1371  * @return なし
1372  */
1373 static void spoiler_print_randart(object_type *o_ptr, obj_desc_list *art_ptr)
1374 {
1375     pval_info_type *pval_ptr = &art_ptr->pval_info;
1376     char buf[80];
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);
1380     } else {
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);
1384         }
1385
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);
1394         }
1395     }
1396
1397     fprintf(spoiler_file, "%s%s\n\n", INDENT1, art_ptr->misc_desc);
1398 }
1399
1400 /*!
1401  * @brief ランダムアーティファクト内容をスポイラー出力するサブルーチン /
1402  * @param player_ptr プレーヤーへの参照ポインタ
1403  * @param o_ptr ランダムアーティファクトのオブジェクト構造体参照ポインタ
1404  * @param i 出力したい記録ランダムアーティファクトID
1405  * @return なし
1406  */
1407 static void spoil_random_artifact_aux(player_type *player_ptr, object_type *o_ptr, int i)
1408 {
1409     obj_desc_list artifact;
1410     if (!object_is_known(o_ptr) || !o_ptr->art_name || o_ptr->tval != group_artifact[i].tval)
1411         return;
1412
1413     random_artifact_analyze(player_ptr, o_ptr, &artifact);
1414     spoiler_print_randart(o_ptr, &artifact);
1415 }
1416
1417 /*!
1418  * @brief ランダムアーティファクト内容をスポイラー出力するメインルーチン /
1419  * Create a list file for random artifacts
1420  * @param fname 出力ファイル名
1421  * @return なし
1422  */
1423 void spoil_random_artifact(player_type *creature_ptr, concptr fname)
1424 {
1425     int i, j;
1426     store_type *store_ptr;
1427     object_type *q_ptr;
1428     char buf[1024];
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.");
1433         return;
1434     }
1435
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);
1442         }
1443
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);
1447         }
1448
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);
1453         }
1454
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);
1459         }
1460     }
1461
1462     if (ferror(spoiler_file) || angband_fclose(spoiler_file)) {
1463         msg_print("Cannot close list file.");
1464         return;
1465     }
1466
1467     msg_print("Successfully created a list file.");
1468 }