OSDN Git Service

Merge pull request #3889 from Slimebreath6078/feature/add_clone_debug_command
[hengbandforosx/hengbandosx.git] / src / info-reader / fixed-map-parser.cpp
1 /*!
2  * @brief ゲームデータ初期化1 / Initialization (part 1) -BEN-
3  * @date 2014/01/28
4  * @author
5  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6  * 2014 Deskull rearranged comment for Doxygen
7  */
8
9 #include "info-reader/fixed-map-parser.h"
10 #include "dungeon/quest.h"
11 #include "floor/fixed-map-generator.h"
12 #include "game-option/birth-options.h"
13 #include "game-option/runtime-arguments.h"
14 #include "info-reader/parse-error-types.h"
15 #include "io/files-util.h"
16 #include "main/init-error-messages-table.h"
17 #include "player-info/class-info.h"
18 #include "player-info/race-info.h"
19 #include "realm/realm-names-table.h"
20 #include "system/angband-exceptions.h"
21 #include "system/angband-system.h"
22 #include "system/floor-type-definition.h"
23 #include "system/player-type-definition.h"
24 #include "util/angband-files.h"
25 #include "util/string-processor.h"
26 #include "view/display-messages.h"
27 #include <algorithm>
28 #include <sstream>
29
30 static concptr variant = "ZANGBAND";
31
32 /*!
33  * @brief 固定マップ (クエスト&街&広域マップ)生成時の分岐処理
34  * Helper function for "parse_fixed_map()"
35  * @param player_ptr プレイヤーへの参照ポインタ
36  * @param sp
37  * @param fp
38  * @return エラーコード
39  */
40 static concptr parse_fixed_map_expression(PlayerType *player_ptr, char **sp, char *fp)
41 {
42     static std::string tmp;
43     char b1 = '[';
44     char b2 = ']';
45
46     char f = ' ';
47
48     char *s;
49     s = (*sp);
50
51     while (iswspace(*s)) {
52         s++;
53     }
54
55     char *b;
56     b = s;
57     concptr v = "?o?o?";
58     if (*s == b1) {
59         concptr p;
60         concptr t;
61         s++;
62         t = parse_fixed_map_expression(player_ptr, &s, &f);
63         if (!*t) {
64             /* Nothing */
65         } else if (streq(t, "IOR")) {
66             v = "0";
67             while (*s && (f != b2)) {
68                 t = parse_fixed_map_expression(player_ptr, &s, &f);
69                 if (*t && !streq(t, "0")) {
70                     v = "1";
71                 }
72             }
73         } else if (streq(t, "AND")) {
74             v = "1";
75             while (*s && (f != b2)) {
76                 t = parse_fixed_map_expression(player_ptr, &s, &f);
77                 if (*t && streq(t, "0")) {
78                     v = "0";
79                 }
80             }
81         } else if (streq(t, "NOT")) {
82             v = "1";
83             while (*s && (f != b2)) {
84                 t = parse_fixed_map_expression(player_ptr, &s, &f);
85                 if (*t && streq(t, "1")) {
86                     v = "0";
87                 }
88             }
89         } else if (streq(t, "EQU")) {
90             v = "0";
91             if (*s && (f != b2)) {
92                 t = parse_fixed_map_expression(player_ptr, &s, &f);
93             }
94
95             while (*s && (f != b2)) {
96                 p = parse_fixed_map_expression(player_ptr, &s, &f);
97                 if (streq(t, p)) {
98                     v = "1";
99                 }
100             }
101         } else if (streq(t, "LEQ")) {
102             v = "1";
103             if (*s && (f != b2)) {
104                 t = parse_fixed_map_expression(player_ptr, &s, &f);
105             }
106
107             while (*s && (f != b2)) {
108                 p = t;
109                 t = parse_fixed_map_expression(player_ptr, &s, &f);
110                 if (*t && atoi(p) > atoi(t)) {
111                     v = "0";
112                 }
113             }
114         } else if (streq(t, "GEQ")) {
115             v = "1";
116             if (*s && (f != b2)) {
117                 t = parse_fixed_map_expression(player_ptr, &s, &f);
118             }
119
120             while (*s && (f != b2)) {
121                 p = t;
122                 t = parse_fixed_map_expression(player_ptr, &s, &f);
123                 if (*t && atoi(p) < atoi(t)) {
124                     v = "0";
125                 }
126             }
127         } else {
128             while (*s && (f != b2)) {
129                 t = parse_fixed_map_expression(player_ptr, &s, &f);
130             }
131         }
132
133         if (f != b2) {
134             v = "?x?x?";
135         }
136         if ((f = *s) != '\0') {
137             *s++ = '\0';
138         }
139
140         (*fp) = f;
141         (*sp) = s;
142         return v;
143     }
144
145 #ifdef JP
146     while (iskanji(*s) || (isprint(*s) && !angband_strchr(" []", *s))) {
147         if (iskanji(*s)) {
148             s++;
149         }
150         s++;
151     }
152 #else
153     while (isprint(*s) && !angband_strchr(" []", *s)) {
154         ++s;
155     }
156 #endif
157     if ((f = *s) != '\0') {
158         *s++ = '\0';
159     }
160
161     if (*b != '$') {
162         v = b;
163         (*fp) = f;
164         (*sp) = s;
165         return v;
166     }
167
168     if (streq(b + 1, "SYS")) {
169         v = ANGBAND_SYS;
170     } else if (streq(b + 1, "GRAF")) {
171         v = ANGBAND_GRAF;
172     } else if (streq(b + 1, "MONOCHROME")) {
173         if (arg_monochrome) {
174             v = "ON";
175         } else {
176             v = "OFF";
177         }
178     } else if (streq(b + 1, "RACE")) {
179         v = _(rp_ptr->E_title, rp_ptr->title);
180     } else if (streq(b + 1, "CLASS")) {
181         v = _(cp_ptr->E_title, cp_ptr->title);
182     } else if (streq(b + 1, "REALM1")) {
183         v = _(E_realm_names[player_ptr->realm1], realm_names[player_ptr->realm1]);
184     } else if (streq(b + 1, "REALM2")) {
185         v = _(E_realm_names[player_ptr->realm2], realm_names[player_ptr->realm2]);
186     } else if (streq(b + 1, "PLAYER")) {
187         static char tmp_player_name[32];
188         char *pn, *tpn;
189         for (pn = player_ptr->name, tpn = tmp_player_name; *pn; pn++, tpn++) {
190 #ifdef JP
191             if (iskanji(*pn)) {
192                 *(tpn++) = *(pn++);
193                 *tpn = *pn;
194                 continue;
195             }
196 #endif
197             *tpn = angband_strchr(" []", *pn) ? '_' : *pn;
198         }
199
200         *tpn = '\0';
201         v = tmp_player_name;
202     } else if (streq(b + 1, "TOWN")) {
203         tmp = std::to_string(player_ptr->town_num);
204         v = tmp.data();
205     } else if (streq(b + 1, "LEVEL")) {
206         tmp = std::to_string(player_ptr->lev);
207         v = tmp.data();
208     } else if (streq(b + 1, "QUEST_NUMBER")) {
209         tmp = std::to_string(enum2i(player_ptr->current_floor_ptr->quest_number));
210         v = tmp.data();
211     } else if (streq(b + 1, "LEAVING_QUEST")) {
212         tmp = std::to_string(enum2i(leaving_quest));
213         v = tmp.data();
214     } else if (prefix(b + 1, "QUEST_TYPE")) {
215         const auto &quest_list = QuestList::get_instance();
216         tmp = std::to_string(enum2i(quest_list[i2enum<QuestId>(atoi(b + 11))].type));
217         v = tmp.data();
218     } else if (prefix(b + 1, "QUEST")) {
219         const auto &quest_list = QuestList::get_instance();
220         tmp = std::to_string(enum2i(quest_list[i2enum<QuestId>(atoi(b + 6))].status));
221         v = tmp.data();
222     } else if (prefix(b + 1, "RANDOM")) {
223         const auto &system = AngbandSystem::get_instance();
224         tmp = std::to_string((static_cast<int>(system.get_seed_town()) % std::stoi(b + 7)));
225         v = tmp.data();
226     } else if (streq(b + 1, "VARIANT")) {
227         v = variant;
228     } else if (streq(b + 1, "WILDERNESS")) {
229         if (vanilla_town) {
230             v = "NONE";
231         } else if (lite_town) {
232             v = "LITE";
233         } else {
234             v = "NORMAL";
235         }
236     } else if (streq(b + 1, "IRONMAN_DOWNWARD")) {
237         v = (ironman_downward ? "1" : "0");
238     }
239
240     (*fp) = f;
241     (*sp) = s;
242     return v;
243 }
244
245 /*!
246  * @brief 固定マップ (クエスト&街&広域マップ)をq_info、t_info、w_infoから読み込んでパースする
247  * @param player_ptr プレイヤーへの参照ポインタ
248  * @param name ファイル名
249  * @param ymin 詳細不明
250  * @param xmin 詳細不明
251  * @param ymax 詳細不明
252  * @param xmax 詳細不明
253  * @return エラーコード
254  */
255 parse_error_type parse_fixed_map(PlayerType *player_ptr, std::string_view name, int ymin, int xmin, int ymax, int xmax)
256 {
257     const auto &path = path_build(ANGBAND_DIR_EDIT, name);
258     auto *fp = angband_fopen(path, FileOpenMode::READ);
259     if (fp == nullptr) {
260         return PARSE_ERROR_GENERIC;
261     }
262
263     int num = -1;
264     parse_error_type err = PARSE_ERROR_NONE;
265     bool bypass = false;
266     int x = xmin;
267     int y = ymin;
268     qtwg_type tmp_qg;
269     char buf[1024]{};
270     qtwg_type *qg_ptr = initialize_quest_generator_type(&tmp_qg, buf, ymin, xmin, ymax, xmax, &y, &x);
271     while (angband_fgets(fp, buf, sizeof(buf)) == 0) {
272         num++;
273         if (!buf[0] || iswspace(buf[0]) || buf[0] == '#') {
274             continue;
275         }
276
277         if ((buf[0] == '?') && (buf[1] == ':')) {
278             char f;
279             char *s;
280             s = buf + 2;
281             concptr v = parse_fixed_map_expression(player_ptr, &s, &f);
282             bypass = streq(v, "0");
283             continue;
284         }
285
286         if (bypass) {
287             continue;
288         }
289
290         err = generate_fixed_map_floor(player_ptr, qg_ptr, parse_fixed_map);
291         if (err != PARSE_ERROR_NONE) {
292             break;
293         }
294     }
295
296     if (err != 0) {
297         concptr oops = (((err > 0) && (err < PARSE_ERROR_MAX)) ? err_str[err] : "unknown");
298         msg_format("Error %d (%s) at line %d of '%s'.", err, oops, num, name.data());
299         msg_format(_("'%s'を解析中。", "Parsing '%s'."), buf);
300         msg_print(nullptr);
301     }
302
303     angband_fclose(fp);
304     return err;
305 }
306
307 static QuestId parse_quest_number_n(const std::vector<std::string> &token)
308 {
309     auto number = i2enum<QuestId>(atoi(token[1].substr(_(0, 1)).data()));
310     return number;
311 }
312
313 static QuestId parse_quest_number(const std::vector<std::string> &token)
314 {
315     auto is_quest_none = _(token[1][0] == '$', token[1][0] != '$');
316     if (is_quest_none) {
317         return QuestId::NONE;
318     }
319
320     if (token[2] == "N") {
321         return parse_quest_number_n(token);
322     }
323     return QuestId::NONE;
324 }
325
326 /*!
327  * @brief クエスト番号をファイルから読み込んでパースする
328  * @param player_ptr プレイヤーへの参照ポインタ
329  * @param file_name ファイル名
330  * @param key_list キーになるQuestIdの配列
331  */
332 static void parse_quest_info_aux(std::string_view file_name, std::set<QuestId> &key_list_ref)
333 {
334     auto push_set = [&key_list_ref, &file_name](auto q, auto line) {
335         if (q == QuestId::NONE) {
336             return;
337         }
338
339         if (key_list_ref.find(q) != key_list_ref.end()) {
340             std::stringstream ss;
341             ss << _("重複したQuestID ", "Duplicated Quest Id ") << enum2i(q) << '(' << file_name << ", L" << line << ')';
342             THROW_EXCEPTION(std::runtime_error, ss.str());
343         }
344
345         key_list_ref.insert(q);
346     };
347
348     const auto &path = path_build(ANGBAND_DIR_EDIT, file_name);
349     auto *fp = angband_fopen(path, FileOpenMode::READ);
350     if (fp == nullptr) {
351         std::stringstream ss;
352         ss << _("ファイルが見つかりません (", "File is not found (") << file_name << ')';
353         THROW_EXCEPTION(std::runtime_error, ss.str());
354     }
355
356     char buf[4096];
357     auto line_num = 0;
358     while (angband_fgets(fp, buf, sizeof(buf)) == 0) {
359         line_num++;
360
361         const auto token = str_split(buf, ':', true);
362
363         switch (token[0][0]) {
364         case 'Q': {
365             auto quest_number = parse_quest_number(token);
366             push_set(quest_number, line_num);
367             break;
368         }
369         case '%': {
370             parse_quest_info_aux(token[1].data(), key_list_ref);
371             break;
372         }
373         default:
374             break;
375         }
376     }
377
378     angband_fclose(fp);
379 }
380
381 /*!
382  * @brief ファイルからパースして作成したクエスト番号配列を返す
383  * @param player_ptr プレイヤーへの参照ポインタ
384  * @param file_name ファイル名
385  * @return クエスト番号の配列
386  */
387 std::set<QuestId> parse_quest_info(std::string_view file_name)
388 {
389     std::set<QuestId> key_list;
390     parse_quest_info_aux(file_name, key_list);
391     return key_list;
392 }