OSDN Git Service

[Implement] 荒野に出現するモンスター一覧のスポイラー出力
[hengbandforosx/hengbandosx.git] / src / wizard / wizard-spoiler.c
1 /*!
2  * @brief スポイラー出力処理 (行数の都合でモンスター進化ツリーもここに入っている)
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  * 2020 Hourier rearranged for decreasing lines.
11  */
12
13 #include "wizard/wizard-spoiler.h"
14 #include "io/files-util.h"
15 #include "io/input-key-acceptor.h"
16 #include "main/sound-of-music.h"
17 #include "monster-race/monster-race.h"
18 #include "monster-race/race-flags8.h"
19 #include "system/angband-version.h"
20 #include "term/screen-processor.h"
21 #include "util/angband-files.h"
22 #include "util/int-char-converter.h"
23 #include "util/sort.h"
24 #include "view/display-messages.h"
25 #include "wizard/fixed-artifacts-spoiler.h"
26 #include "wizard/items-spoiler.h"
27 #include "wizard/monster-info-spoiler.h"
28 #include "wizard/spoiler-util.h"
29
30 /*!
31  * @brief int配列でstrncmp()と似た比較処理を行う /
32  * Compare two int-type array like strncmp() and return TRUE if equals
33  * @param a 比較するint配列1
34  * @param b 比較するint配列2
35  * @param length
36  * @return 両者の値が等しければTRUEを返す
37  */
38 static bool int_n_cmp(int *a, int *b, int length)
39 {
40     if (!length)
41         return TRUE;
42
43     do {
44         if (*a != *(b++))
45             return FALSE;
46         if (!(*(a++)))
47             break;
48     } while (--length);
49
50     return TRUE;
51 }
52
53 /*!
54  * @brief ある木が指定された木の部分木かどうかを返す /
55  * Returns TRUE if an evolution tree is "partial tree"
56  * @param tree 元となる木構造リスト
57  * @param partial_tree 部分木かどうか判定したい木構造リスト
58  * @return 部分木ならばTRUEを返す
59  */
60 static bool is_partial_tree(int *tree, int *partial_tree)
61 {
62     int pt_head = *(partial_tree++);
63     int pt_len = 0;
64     while (partial_tree[pt_len])
65         pt_len++;
66
67     while (*tree) {
68         if (*(tree++) == pt_head) {
69             if (int_n_cmp(tree, partial_tree, pt_len))
70                 return TRUE;
71         }
72     }
73
74     return FALSE;
75 }
76
77 /*!
78  * @brief 進化ツリーをスポイラー出力するメインルーチン /
79  * Print monsters' evolution information to file
80  * @param fname 出力ファイル名
81  * @return なし
82  */
83 static spoiler_output_status spoil_mon_evol(concptr fname)
84 {
85     char buf[1024];
86     monster_race *r_ptr;
87     player_type dummy;
88     int **evol_tree, i, j, n, r_idx;
89     int *evol_tree_zero; /* For C_KILL() */
90     path_build(buf, sizeof buf, ANGBAND_DIR_USER, fname);
91     spoiler_file = angband_fopen(buf, "w");
92     if (!spoiler_file) {
93         return SPOILER_OUTPUT_FAIL_FOPEN;
94     }
95
96     char title[200];
97     put_version(title);
98     sprintf(buf, "Monster Spoilers for %s\n", title);
99     spoil_out(buf);
100
101     spoil_out("------------------------------------------\n\n");
102     C_MAKE(evol_tree, max_r_idx, int *);
103     C_MAKE(*evol_tree, max_r_idx * (max_evolution_depth + 1), int);
104     for (i = 1; i < max_r_idx; i++)
105         evol_tree[i] = *evol_tree + i * (max_evolution_depth + 1);
106
107     evol_tree_zero = *evol_tree;
108     for (i = 1; i < max_r_idx; i++) {
109         r_ptr = &r_info[i];
110         if (!r_ptr->next_exp)
111             continue;
112
113         n = 0;
114         evol_tree[i][n++] = i;
115         do {
116             evol_tree[i][n++] = r_ptr->next_r_idx;
117             r_ptr = &r_info[r_ptr->next_r_idx];
118         } while (r_ptr->next_exp && (n < max_evolution_depth));
119     }
120
121     for (i = 1; i < max_r_idx; i++) {
122         if (!evol_tree[i][0])
123             continue;
124
125         for (j = 1; j < max_r_idx; j++) {
126             if (i == j)
127                 continue;
128
129             if (!evol_tree[j][0])
130                 continue;
131
132             if (is_partial_tree(evol_tree[j], evol_tree[i])) {
133                 evol_tree[i][0] = 0;
134                 break;
135             }
136         }
137     }
138
139     ang_sort(&dummy, evol_tree, NULL, max_r_idx, ang_sort_comp_evol_tree, ang_sort_swap_evol_tree);
140     for (i = 0; i < max_r_idx; i++) {
141         r_idx = evol_tree[i][0];
142         if (!r_idx)
143             continue;
144
145         r_ptr = &r_info[r_idx];
146         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);
147         for (n = 1; r_ptr->next_exp; n++) {
148             fprintf(spoiler_file, "%*s-(%ld)-> ", n * 2, "", (long int)r_ptr->next_exp);
149             fprintf(spoiler_file, "[%d]: ", r_ptr->next_r_idx);
150             r_ptr = &r_info[r_ptr->next_r_idx];
151             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);
152         }
153
154         fputc('\n', spoiler_file);
155     }
156
157     C_KILL(evol_tree_zero, max_r_idx * (max_evolution_depth + 1), int);
158     C_KILL(evol_tree, max_r_idx, int *);
159     if (ferror(spoiler_file) || angband_fclose(spoiler_file)) {
160         return SPOILER_OUTPUT_FAIL_FCLOSE;
161     }
162     return SPOILER_OUTPUT_SUCCESS;
163 }
164
165 /*!
166  * @brief スポイラー出力を行うコマンドのメインルーチン /
167  * Create Spoiler files -BEN-
168  * @return なし
169  */
170 void exe_output_spoilers(void)
171 {
172     screen_save();
173     while (TRUE) {
174         spoiler_output_status status = SPOILER_OUTPUT_CANCEL;
175         term_clear();
176         prt("Create a spoiler file.", 2, 0);
177         prt("(1) Brief Object Info (obj-desc.txt)", 5, 5);
178         prt("(2) Brief Artifact Info (artifact.txt)", 6, 5);
179         prt("(3) Brief Monster Info (mon-desc.txt)", 7, 5);
180         prt("(4) Full Monster Info (mon-info.txt)", 8, 5);
181         prt("(5) Monster Evolution Info (mon-evol.txt)", 9, 5);
182         prt(_("コマンド:", "Command: "), _(18, 12), 0);
183         switch (inkey()) {
184         case ESCAPE:
185             screen_load();
186             return;
187         case '1':
188             status = spoil_obj_desc("obj-desc.txt");
189             break;
190         case '2':
191             status = spoil_fixed_artifact("artifact.txt");
192             break;
193         case '3':
194             status = spoil_mon_desc_all("mon-desc.txt");
195             break;
196         case '4':
197             status = spoil_mon_info("mon-info.txt");
198             break;
199         case '5':
200             status = spoil_mon_evol("mon-evol.txt");
201             break;
202         default:
203             bell();
204             break;
205         }
206
207         switch (status) {
208         case SPOILER_OUTPUT_FAIL_FOPEN:
209             msg_print("Cannot create spoiler file.");
210             break;
211         case SPOILER_OUTPUT_FAIL_FCLOSE:
212             msg_print("Cannot close spoiler file.");
213             break;
214         case SPOILER_OUTPUT_SUCCESS:
215             msg_print("Successfully created a spoiler file.");
216             break;
217         }
218         msg_erase();
219     }
220 }
221
222 /*!
223  * @brief 全スポイラー出力を行うコマンドのメインルーチン /
224  * Create Spoiler files -BEN-
225  * @return 成功時SPOILER_OUTPUT_SUCCESS / 失敗時エラー状態
226  */
227 spoiler_output_status output_all_spoilers(void)
228 {
229     spoiler_output_status status;
230     status = spoil_obj_desc("obj-desc.txt");
231     if (status != SPOILER_OUTPUT_SUCCESS)
232         return status;
233
234     status = spoil_fixed_artifact("artifact.txt");
235     if (status != SPOILER_OUTPUT_SUCCESS)
236         return status;
237
238     status = spoil_mon_desc_all("mon-desc.txt");
239     if (status != SPOILER_OUTPUT_SUCCESS)
240         return status;
241
242     status = spoil_mon_desc("mon-desc-wildonly.txt", FALSE, RF8_WILD_ONLY);
243     if (status != SPOILER_OUTPUT_SUCCESS)
244         return status;
245     status = spoil_mon_desc("mon-desc-town.txt", FALSE, RF8_WILD_TOWN);
246     if (status != SPOILER_OUTPUT_SUCCESS)
247         return status;
248     status = spoil_mon_desc("mon-desc-shore.txt", FALSE, RF8_WILD_SHORE);
249     if (status != SPOILER_OUTPUT_SUCCESS)
250         return status;
251     status = spoil_mon_desc("mon-desc-ocean.txt", FALSE, RF8_WILD_OCEAN);
252     if (status != SPOILER_OUTPUT_SUCCESS)
253         return status;
254     status = spoil_mon_desc("mon-desc-waste.txt", FALSE, RF8_WILD_WASTE);
255     if (status != SPOILER_OUTPUT_SUCCESS)
256         return status;
257     status = spoil_mon_desc("mon-desc-wood.txt", FALSE, RF8_WILD_WOOD);
258     if (status != SPOILER_OUTPUT_SUCCESS)
259         return status;
260     status = spoil_mon_desc("mon-desc-volcano.txt", FALSE, RF8_WILD_VOLCANO);
261     if (status != SPOILER_OUTPUT_SUCCESS)
262         return status;
263     status = spoil_mon_desc("mon-desc-mountain.txt", FALSE, RF8_WILD_MOUNTAIN);
264     if (status != SPOILER_OUTPUT_SUCCESS)
265         return status;
266     status = spoil_mon_desc("mon-desc-grass.txt", FALSE, RF8_WILD_GRASS);
267     if (status != SPOILER_OUTPUT_SUCCESS)
268         return status;
269     status = spoil_mon_desc("mon-desc-wildall.txt", FALSE, RF8_WILD_ALL);
270     if (status != SPOILER_OUTPUT_SUCCESS)
271         return status;
272
273     status = spoil_mon_info("mon-info.txt");
274     if (status != SPOILER_OUTPUT_SUCCESS)
275         return status;
276
277     status = spoil_mon_evol("mon-evol.txt");
278     if (status != SPOILER_OUTPUT_SUCCESS)
279         return status;
280
281     return SPOILER_OUTPUT_SUCCESS;
282 }