OSDN Git Service

Refactor monster_desc() to avoid sprintf(). To work with the refactored monster_desc...
[hengbandforosx/hengbandosx.git] / src / target / target-describer.cpp
1 #include "target/target-describer.h"
2 #include "action/travel-execution.h"
3 #include "core/stuff-handler.h"
4 #include "dungeon/quest.h"
5 #include "flavor/flavor-describer.h"
6 #include "floor/cave.h"
7 #include "floor/floor-object.h"
8 #include "floor/floor-town.h"
9 #include "floor/geometry.h"
10 #include "floor/object-scanner.h"
11 #include "game-option/input-options.h"
12 #include "grid/feature.h"
13 #include "grid/grid.h"
14 #include "info-reader/fixed-map-parser.h"
15 #include "io/cursor.h"
16 #include "io/input-key-acceptor.h"
17 #include "locale/english.h"
18 #include "lore/lore-util.h"
19 #include "monster-race/monster-race.h"
20 #include "monster-race/race-flags1.h"
21 #include "monster/monster-describer.h"
22 #include "monster/monster-description-types.h"
23 #include "monster/monster-flag-types.h"
24 #include "object/item-tester-hooker.h"
25 #include "object/object-mark-types.h"
26 #include "player-base/player-race.h"
27 #include "player/player-status-table.h"
28 #include "system/building-type-definition.h"
29 #include "system/dungeon-info.h"
30 #include "system/floor-type-definition.h"
31 #include "system/grid-type-definition.h"
32 #include "system/item-entity.h"
33 #include "system/monster-entity.h"
34 #include "system/monster-race-info.h"
35 #include "system/player-type-definition.h"
36 #include "system/system-variables.h"
37 #include "system/terrain-type-definition.h"
38 #include "target/target-types.h"
39 #include "term/screen-processor.h"
40 #include "term/term-color-types.h"
41 #include "term/z-form.h"
42 #include "timed-effect/player-hallucination.h"
43 #include "timed-effect/timed-effects.h"
44 #include "util/bit-flags-calculator.h"
45 #include "view/display-lore.h"
46 #include "view/display-messages.h"
47 #include "view/display-monster-status.h"
48 #include "window/display-sub-windows.h"
49 #include "world/world.h"
50
51 static const int16_t CONTINUOUS_DESCRIPTION = 256;
52
53 bool show_gold_on_floor = false;
54
55 // Examine grid
56 struct eg_type {
57     POSITION y;
58     POSITION x;
59     target_type mode;
60     bool boring;
61     concptr info;
62     concptr s1;
63     concptr s2;
64     concptr s3;
65     concptr x_info;
66     char query;
67     char out_val[MAX_NLEN + 80];
68     OBJECT_IDX floor_list[23];
69     ITEM_NUMBER floor_num;
70     grid_type *g_ptr;
71     MonsterEntity *m_ptr;
72     OBJECT_IDX next_o_idx;
73     FEAT_IDX feat;
74     TerrainType *f_ptr;
75     std::string name;
76 };
77
78 static eg_type *initialize_eg_type(PlayerType *player_ptr, eg_type *eg_ptr, POSITION y, POSITION x, target_type mode, concptr info)
79 {
80     eg_ptr->y = y;
81     eg_ptr->x = x;
82     eg_ptr->boring = true;
83     eg_ptr->mode = mode;
84     eg_ptr->info = info;
85     eg_ptr->s1 = "";
86     eg_ptr->s2 = "";
87     eg_ptr->s3 = "";
88     eg_ptr->x_info = "";
89     eg_ptr->query = '\001';
90     eg_ptr->floor_num = 0;
91
92     auto *floor_ptr = player_ptr->current_floor_ptr;
93     eg_ptr->g_ptr = &floor_ptr->grid_array[y][x];
94     eg_ptr->m_ptr = &floor_ptr->m_list[eg_ptr->g_ptr->m_idx];
95     eg_ptr->next_o_idx = 0;
96     return eg_ptr;
97 }
98
99 /*
100  * Evaluate number of kill needed to gain level
101  */
102 static std::string evaluate_monster_exp(PlayerType *player_ptr, MonsterEntity *m_ptr)
103 {
104     MonsterRaceInfo *ap_r_ptr = &monraces_info[m_ptr->ap_r_idx];
105     if ((player_ptr->lev >= PY_MAX_LEVEL) || PlayerRace(player_ptr).equals(PlayerRaceType::ANDROID)) {
106         return "**";
107     }
108
109     if (!ap_r_ptr->r_tkills || m_ptr->mflag2.has(MonsterConstantFlagType::KAGE)) {
110         if (!w_ptr->wizard) {
111             return "??";
112         }
113     }
114
115     int32_t exp_mon = ap_r_ptr->mexp * ap_r_ptr->level;
116     uint32_t exp_mon_frac = 0;
117     s64b_div(&exp_mon, &exp_mon_frac, 0, (player_ptr->max_plv + 2));
118
119     int32_t exp_adv = player_exp[player_ptr->lev - 1] * player_ptr->expfact;
120     uint32_t exp_adv_frac = 0;
121     s64b_div(&exp_adv, &exp_adv_frac, 0, 100);
122
123     s64b_sub(&exp_adv, &exp_adv_frac, player_ptr->exp, player_ptr->exp_frac);
124
125     s64b_add(&exp_adv, &exp_adv_frac, exp_mon, exp_mon_frac);
126     s64b_sub(&exp_adv, &exp_adv_frac, 0, 1);
127
128     s64b_div(&exp_adv, &exp_adv_frac, exp_mon, exp_mon_frac);
129
130     auto num = std::min<uint>(999, exp_adv_frac);
131     return format("%03ld", (long int)num);
132 }
133
134 static void describe_scan_result(PlayerType *player_ptr, eg_type *eg_ptr)
135 {
136     if (!easy_floor) {
137         return;
138     }
139
140     eg_ptr->floor_num = scan_floor_items(player_ptr, eg_ptr->floor_list, eg_ptr->y, eg_ptr->x, SCAN_FLOOR_ONLY_MARKED, AllMatchItemTester());
141     if (eg_ptr->floor_num > 0) {
142         eg_ptr->x_info = _("x物 ", "x,");
143     }
144 }
145
146 static void describe_target(PlayerType *player_ptr, eg_type *eg_ptr)
147 {
148     if (!player_bold(player_ptr, eg_ptr->y, eg_ptr->x)) {
149         eg_ptr->s1 = _("ターゲット:", "Target:");
150         return;
151     }
152
153 #ifdef JP
154     eg_ptr->s1 = "あなたは";
155     eg_ptr->s2 = "の上";
156     eg_ptr->s3 = "にいる";
157 #else
158     eg_ptr->s1 = "You are ";
159     eg_ptr->s2 = "on ";
160 #endif
161 }
162
163 static ProcessResult describe_hallucinated_target(PlayerType *player_ptr, eg_type *eg_ptr)
164 {
165     if (!player_ptr->effects()->hallucination()->is_hallucinated()) {
166         return ProcessResult::PROCESS_CONTINUE;
167     }
168
169     concptr name = _("何か奇妙な物", "something strange");
170 #ifdef JP
171     strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s [%s]", eg_ptr->s1, name, eg_ptr->s2, eg_ptr->s3, eg_ptr->info);
172 #else
173     strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s [%s]", eg_ptr->s1, eg_ptr->s2, eg_ptr->s3, name, eg_ptr->info);
174 #endif
175     prt(eg_ptr->out_val, 0, 0);
176     move_cursor_relative(eg_ptr->y, eg_ptr->x);
177     eg_ptr->query = inkey();
178     if ((eg_ptr->query != '\r') && (eg_ptr->query != '\n')) {
179         return ProcessResult::PROCESS_TRUE;
180     }
181
182     return ProcessResult::PROCESS_FALSE;
183 }
184
185 static bool describe_grid_lore(PlayerType *player_ptr, eg_type *eg_ptr)
186 {
187     screen_save();
188     screen_roff(player_ptr, eg_ptr->m_ptr->ap_r_idx, MONSTER_LORE_NORMAL);
189     term_addstr(-1, TERM_WHITE, format(_("  [r思 %s%s]", "  [r,%s%s]"), eg_ptr->x_info, eg_ptr->info));
190     eg_ptr->query = inkey();
191     screen_load();
192     return eg_ptr->query != 'r';
193 }
194
195 static void describe_grid_monster(PlayerType *player_ptr, eg_type *eg_ptr)
196 {
197     bool recall = false;
198     const auto m_name = monster_desc(player_ptr, eg_ptr->m_ptr, MD_INDEF_VISIBLE);
199     while (true) {
200         if (recall) {
201             if (describe_grid_lore(player_ptr, eg_ptr)) {
202                 return;
203             }
204
205             recall = false;
206             continue;
207         }
208
209         std::string acount = evaluate_monster_exp(player_ptr, eg_ptr->m_ptr);
210         const auto mon_desc = look_mon_desc(eg_ptr->m_ptr, 0x01);
211 #ifdef JP
212         strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "[%s]%s%s(%s)%s%s [r思 %s%s]", acount.data(), eg_ptr->s1, m_name.data(), mon_desc.data(), eg_ptr->s2, eg_ptr->s3,
213             eg_ptr->x_info, eg_ptr->info);
214 #else
215         strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "[%s]%s%s%s%s(%s) [r, %s%s]", acount.data(), eg_ptr->s1, eg_ptr->s2, eg_ptr->s3, m_name.data(), mon_desc.data(),
216             eg_ptr->x_info, eg_ptr->info);
217 #endif
218         prt(eg_ptr->out_val, 0, 0);
219         move_cursor_relative(eg_ptr->y, eg_ptr->x);
220         eg_ptr->query = inkey();
221         if (eg_ptr->query != 'r') {
222             return;
223         }
224
225         recall = true;
226     }
227 }
228
229 static void describe_monster_person(eg_type *eg_ptr)
230 {
231     MonsterRaceInfo *ap_r_ptr = &monraces_info[eg_ptr->m_ptr->ap_r_idx];
232     eg_ptr->s1 = _("それは", "It is ");
233     if (ap_r_ptr->flags1 & RF1_FEMALE) {
234         eg_ptr->s1 = _("彼女は", "She is ");
235     } else if (ap_r_ptr->flags1 & RF1_MALE) {
236         eg_ptr->s1 = _("彼は", "He is ");
237     }
238
239 #ifdef JP
240     eg_ptr->s2 = "を";
241     eg_ptr->s3 = "持っている";
242 #else
243     eg_ptr->s2 = "carrying ";
244 #endif
245 }
246
247 static uint16_t describe_monster_item(PlayerType *player_ptr, eg_type *eg_ptr)
248 {
249     for (const auto this_o_idx : eg_ptr->m_ptr->hold_o_idx_list) {
250         GAME_TEXT o_name[MAX_NLEN];
251         ItemEntity *o_ptr;
252         o_ptr = &player_ptr->current_floor_ptr->o_list[this_o_idx];
253         describe_flavor(player_ptr, o_name, o_ptr, 0);
254 #ifdef JP
255         strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s[%s]", eg_ptr->s1, o_name, eg_ptr->s2, eg_ptr->s3, eg_ptr->info);
256 #else
257         strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s [%s]", eg_ptr->s1, eg_ptr->s2, eg_ptr->s3, o_name, eg_ptr->info);
258 #endif
259         prt(eg_ptr->out_val, 0, 0);
260         move_cursor_relative(eg_ptr->y, eg_ptr->x);
261         eg_ptr->query = inkey();
262         if ((eg_ptr->query != '\r') && (eg_ptr->query != '\n') && (eg_ptr->query != ' ') && (eg_ptr->query != 'x')) {
263             return eg_ptr->query;
264         }
265
266         if ((eg_ptr->query == ' ') && !(eg_ptr->mode & TARGET_LOOK)) {
267             return eg_ptr->query;
268         }
269
270         eg_ptr->s2 = _("をまた", "also carrying ");
271     }
272
273     return CONTINUOUS_DESCRIPTION;
274 }
275
276 static bool within_char_util(int16_t input)
277 {
278     return (input > -127) && (input < 128);
279 }
280
281 static int16_t describe_grid(PlayerType *player_ptr, eg_type *eg_ptr)
282 {
283     if ((eg_ptr->g_ptr->m_idx == 0) || !player_ptr->current_floor_ptr->m_list[eg_ptr->g_ptr->m_idx].ml) {
284         return CONTINUOUS_DESCRIPTION;
285     }
286
287     eg_ptr->boring = false;
288     monster_race_track(player_ptr, eg_ptr->m_ptr->ap_r_idx);
289     health_track(player_ptr, eg_ptr->g_ptr->m_idx);
290     handle_stuff(player_ptr);
291     describe_grid_monster(player_ptr, eg_ptr);
292     if ((eg_ptr->query != '\r') && (eg_ptr->query != '\n') && (eg_ptr->query != ' ') && (eg_ptr->query != 'x')) {
293         return eg_ptr->query;
294     }
295
296     if ((eg_ptr->query == ' ') && !(eg_ptr->mode & TARGET_LOOK)) {
297         return eg_ptr->query;
298     }
299
300     describe_monster_person(eg_ptr);
301     uint16_t monster_item_description = describe_monster_item(player_ptr, eg_ptr);
302     if (within_char_util(monster_item_description)) {
303         return (char)monster_item_description;
304     }
305
306 #ifdef JP
307     eg_ptr->s2 = "の上";
308     eg_ptr->s3 = "にいる";
309 #else
310     eg_ptr->s2 = "on ";
311 #endif
312     return CONTINUOUS_DESCRIPTION;
313 }
314
315 static int16_t describe_footing(PlayerType *player_ptr, eg_type *eg_ptr)
316 {
317     if (eg_ptr->floor_num != 1) {
318         return CONTINUOUS_DESCRIPTION;
319     }
320
321     GAME_TEXT o_name[MAX_NLEN];
322     ItemEntity *o_ptr;
323     o_ptr = &player_ptr->current_floor_ptr->o_list[eg_ptr->floor_list[0]];
324     describe_flavor(player_ptr, o_name, o_ptr, 0);
325 #ifdef JP
326     strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s[%s]", eg_ptr->s1, o_name, eg_ptr->s2, eg_ptr->s3, eg_ptr->info);
327 #else
328     strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s [%s]", eg_ptr->s1, eg_ptr->s2, eg_ptr->s3, o_name, eg_ptr->info);
329 #endif
330     prt(eg_ptr->out_val, 0, 0);
331     move_cursor_relative(eg_ptr->y, eg_ptr->x);
332     eg_ptr->query = inkey();
333     return eg_ptr->query;
334 }
335
336 static int16_t describe_footing_items(eg_type *eg_ptr)
337 {
338     if (!eg_ptr->boring) {
339         return CONTINUOUS_DESCRIPTION;
340     }
341
342 #ifdef JP
343     strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s %d個のアイテム%s%s ['x'で一覧, %s]", eg_ptr->s1, (int)eg_ptr->floor_num, eg_ptr->s2, eg_ptr->s3, eg_ptr->info);
344 #else
345     strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%sa pile of %d items [x,%s]", eg_ptr->s1, eg_ptr->s2, eg_ptr->s3, (int)eg_ptr->floor_num, eg_ptr->info);
346 #endif
347     prt(eg_ptr->out_val, 0, 0);
348     move_cursor_relative(eg_ptr->y, eg_ptr->x);
349     eg_ptr->query = inkey();
350     if (eg_ptr->query != 'x' && eg_ptr->query != ' ') {
351         return eg_ptr->query;
352     }
353
354     return CONTINUOUS_DESCRIPTION;
355 }
356
357 static char describe_footing_many_items(PlayerType *player_ptr, eg_type *eg_ptr, int *min_width)
358 {
359     while (true) {
360         screen_save();
361         show_gold_on_floor = true;
362         (void)show_floor_items(player_ptr, 0, eg_ptr->y, eg_ptr->x, min_width, AllMatchItemTester());
363         show_gold_on_floor = false;
364 #ifdef JP
365         strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s %d個のアイテム%s%s [Enterで次へ, %s]", eg_ptr->s1, (int)eg_ptr->floor_num, eg_ptr->s2, eg_ptr->s3, eg_ptr->info);
366 #else
367         strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%sa pile of %d items [Enter,%s]", eg_ptr->s1, eg_ptr->s2, eg_ptr->s3, (int)eg_ptr->floor_num, eg_ptr->info);
368 #endif
369         prt(eg_ptr->out_val, 0, 0);
370         eg_ptr->query = inkey();
371         screen_load();
372         if (eg_ptr->query != '\n' && eg_ptr->query != '\r') {
373             return eg_ptr->query;
374         }
375
376         if (eg_ptr->g_ptr->o_idx_list.size() < 2) {
377             continue;
378         }
379
380         eg_ptr->g_ptr->o_idx_list.rotate(player_ptr->current_floor_ptr);
381
382         // ターゲットしている床の座標を渡す必要があるので、window_stuff経由ではなく直接呼び出す
383         fix_floor_item_list(player_ptr, eg_ptr->y, eg_ptr->x);
384     }
385 }
386
387 static int16_t loop_describing_grid(PlayerType *player_ptr, eg_type *eg_ptr)
388 {
389     if (eg_ptr->floor_num == 0) {
390         return CONTINUOUS_DESCRIPTION;
391     }
392
393     int min_width = 0;
394     while (true) {
395         int16_t footing_description = describe_footing(player_ptr, eg_ptr);
396         if (within_char_util(footing_description)) {
397             return (char)footing_description;
398         }
399
400         int16_t footing_descriptions = describe_footing_items(eg_ptr);
401         if (within_char_util(footing_descriptions)) {
402             return (char)footing_descriptions;
403         }
404
405         return describe_footing_many_items(player_ptr, eg_ptr, &min_width);
406     }
407 }
408
409 static int16_t describe_footing_sight(PlayerType *player_ptr, eg_type *eg_ptr, ItemEntity *o_ptr)
410 {
411     if (o_ptr->marked.has_not(OmType::FOUND)) {
412         return CONTINUOUS_DESCRIPTION;
413     }
414
415     GAME_TEXT o_name[MAX_NLEN];
416     eg_ptr->boring = false;
417     describe_flavor(player_ptr, o_name, o_ptr, 0);
418 #ifdef JP
419     strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s[%s]", eg_ptr->s1, o_name, eg_ptr->s2, eg_ptr->s3, eg_ptr->info);
420 #else
421     strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s [%s]", eg_ptr->s1, eg_ptr->s2, eg_ptr->s3, o_name, eg_ptr->info);
422 #endif
423     prt(eg_ptr->out_val, 0, 0);
424     move_cursor_relative(eg_ptr->y, eg_ptr->x);
425     eg_ptr->query = inkey();
426     if ((eg_ptr->query != '\r') && (eg_ptr->query != '\n') && (eg_ptr->query != ' ') && (eg_ptr->query != 'x')) {
427         return eg_ptr->query;
428     }
429
430     if ((eg_ptr->query == ' ') && !(eg_ptr->mode & TARGET_LOOK)) {
431         return eg_ptr->query;
432     }
433
434     eg_ptr->s1 = _("それは", "It is ");
435     if (o_ptr->number != 1) {
436         eg_ptr->s1 = _("それらは", "They are ");
437     }
438
439 #ifdef JP
440     eg_ptr->s2 = "の上";
441     eg_ptr->s3 = "に見える";
442 #else
443     eg_ptr->s2 = "on ";
444 #endif
445     return CONTINUOUS_DESCRIPTION;
446 }
447
448 static int16_t sweep_footing_items(PlayerType *player_ptr, eg_type *eg_ptr)
449 {
450     for (const auto this_o_idx : eg_ptr->g_ptr->o_idx_list) {
451         ItemEntity *o_ptr;
452         o_ptr = &player_ptr->current_floor_ptr->o_list[this_o_idx];
453         int16_t ret = describe_footing_sight(player_ptr, eg_ptr, o_ptr);
454         if (within_char_util(ret)) {
455             return (char)ret;
456         }
457     }
458
459     return CONTINUOUS_DESCRIPTION;
460 }
461
462 static std::string decide_target_floor(PlayerType *player_ptr, eg_type *eg_ptr)
463 {
464     if (eg_ptr->f_ptr->flags.has(TerrainCharacteristics::QUEST_ENTER)) {
465         QuestId old_quest = player_ptr->current_floor_ptr->quest_number;
466         const auto &quest_list = QuestList::get_instance();
467         const QuestId number = i2enum<QuestId>(eg_ptr->g_ptr->special);
468         const auto *q_ptr = &quest_list[number];
469         std::string_view msg(_("クエスト「%s」(%d階相当)", "the entrance to the quest '%s'(level %d)"));
470         for (int j = 0; j < 10; j++) {
471             quest_text[j][0] = '\0';
472         }
473
474         quest_text_line = 0;
475         player_ptr->current_floor_ptr->quest_number = number;
476         init_flags = INIT_NAME_ONLY;
477         parse_fixed_map(player_ptr, QUEST_DEFINITION_LIST, 0, 0, 0, 0);
478         player_ptr->current_floor_ptr->quest_number = old_quest;
479         return format(msg.data(), q_ptr->name, q_ptr->level);
480     }
481
482     if (eg_ptr->f_ptr->flags.has(TerrainCharacteristics::BLDG) && !player_ptr->current_floor_ptr->inside_arena) {
483         return building[eg_ptr->f_ptr->subtype].name;
484     }
485
486     if (eg_ptr->f_ptr->flags.has(TerrainCharacteristics::ENTRANCE)) {
487         return format(_("%s(%d階相当)", "%s(level %d)"), dungeons_info[eg_ptr->g_ptr->special].text.data(), dungeons_info[eg_ptr->g_ptr->special].mindepth);
488     }
489
490     if (eg_ptr->f_ptr->flags.has(TerrainCharacteristics::TOWN)) {
491         return town_info[eg_ptr->g_ptr->special].name;
492     }
493
494     if (player_ptr->wild_mode && (eg_ptr->feat == feat_floor)) {
495         return _("道", "road");
496     }
497
498     return eg_ptr->f_ptr->name.data();
499 }
500
501 static void describe_grid_monster_all(eg_type *eg_ptr)
502 {
503     if (!w_ptr->wizard) {
504 #ifdef JP
505         strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s[%s]", eg_ptr->s1, eg_ptr->name.data(), eg_ptr->s2, eg_ptr->s3, eg_ptr->info);
506 #else
507         strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s [%s]", eg_ptr->s1, eg_ptr->s2, eg_ptr->s3, eg_ptr->name.data(), eg_ptr->info);
508 #endif
509         return;
510     }
511
512     std::string f_idx_str;
513     if (eg_ptr->g_ptr->mimic) {
514         f_idx_str = format("%d/%d", eg_ptr->g_ptr->feat, eg_ptr->g_ptr->mimic);
515     } else {
516         f_idx_str = std::to_string(eg_ptr->g_ptr->feat);
517     }
518
519 #ifdef JP
520     strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s[%s] %x %s %d %d %d (%d,%d) %d", eg_ptr->s1, eg_ptr->name.data(), eg_ptr->s2, eg_ptr->s3, eg_ptr->info,
521         (uint)eg_ptr->g_ptr->info, f_idx_str.data(), eg_ptr->g_ptr->dists[FLOW_NORMAL], eg_ptr->g_ptr->costs[FLOW_NORMAL], eg_ptr->g_ptr->when, (int)eg_ptr->y,
522         (int)eg_ptr->x, travel.cost[eg_ptr->y][eg_ptr->x]);
523 #else
524     strnfmt(eg_ptr->out_val, sizeof(eg_ptr->out_val), "%s%s%s%s [%s] %x %s %d %d %d (%d,%d)", eg_ptr->s1, eg_ptr->s2, eg_ptr->s3, eg_ptr->name.data(), eg_ptr->info, eg_ptr->g_ptr->info,
525         f_idx_str.data(), eg_ptr->g_ptr->dists[FLOW_NORMAL], eg_ptr->g_ptr->costs[FLOW_NORMAL], eg_ptr->g_ptr->when, (int)eg_ptr->y, (int)eg_ptr->x);
526 #endif
527 }
528
529 /*!
530  * @brief xまたはlで指定したグリッドにあるアイテムやモンスターの説明を記述する
531  * @param player_ptr プレイヤーへの参照ポインタ
532  * @param y 指定グリッドのY座標
533  * @param x 指定グリッドのX座標
534  * @param mode x (KILL)かl (LOOK)
535  * @param info 記述用文字列
536  * @return 入力キー
537  * @todo xとlで処理を分ける?
538  */
539 char examine_grid(PlayerType *player_ptr, const POSITION y, const POSITION x, target_type mode, concptr info)
540 {
541     eg_type tmp_eg;
542     eg_type *eg_ptr = initialize_eg_type(player_ptr, &tmp_eg, y, x, mode, info);
543     describe_scan_result(player_ptr, eg_ptr);
544     describe_target(player_ptr, eg_ptr);
545     ProcessResult next_target = describe_hallucinated_target(player_ptr, eg_ptr);
546     switch (next_target) {
547     case ProcessResult::PROCESS_FALSE:
548         return 0;
549     case ProcessResult::PROCESS_TRUE:
550         return eg_ptr->query;
551     default:
552         break;
553     }
554
555     int16_t description_grid = describe_grid(player_ptr, eg_ptr);
556     if (within_char_util(description_grid)) {
557         return (char)description_grid;
558     }
559
560     int16_t loop_description = loop_describing_grid(player_ptr, eg_ptr);
561     if (within_char_util(loop_description)) {
562         return (char)loop_description;
563     }
564
565     int16_t footing_items_description = sweep_footing_items(player_ptr, eg_ptr);
566     if (within_char_util(footing_items_description)) {
567         return (char)footing_items_description;
568     }
569
570     eg_ptr->feat = eg_ptr->g_ptr->get_feat_mimic();
571     if (!eg_ptr->g_ptr->is_mark() && !player_can_see_bold(player_ptr, y, x)) {
572         eg_ptr->feat = feat_none;
573     }
574
575     eg_ptr->f_ptr = &terrains_info[eg_ptr->feat];
576     if (!eg_ptr->boring && eg_ptr->f_ptr->flags.has_not(TerrainCharacteristics::REMEMBER)) {
577         return (eg_ptr->query != '\r') && (eg_ptr->query != '\n') ? eg_ptr->query : 0;
578     }
579
580     /*
581      * グローバル変数への代入をここで行っているので動かしたくない
582      * 安全を確保できたら構造体から外すことも検討する
583      */
584     eg_ptr->name = decide_target_floor(player_ptr, eg_ptr);
585     if (*eg_ptr->s2 && (eg_ptr->f_ptr->flags.has_none_of({ TerrainCharacteristics::MOVE, TerrainCharacteristics::CAN_FLY }) || eg_ptr->f_ptr->flags.has_none_of({ TerrainCharacteristics::LOS, TerrainCharacteristics::TREE }) || eg_ptr->f_ptr->flags.has(TerrainCharacteristics::TOWN))) {
586         eg_ptr->s2 = _("の中", "in ");
587     }
588
589     if (eg_ptr->f_ptr->flags.has(TerrainCharacteristics::STORE) || eg_ptr->f_ptr->flags.has(TerrainCharacteristics::QUEST_ENTER) || (eg_ptr->f_ptr->flags.has(TerrainCharacteristics::BLDG) && !player_ptr->current_floor_ptr->inside_arena) || eg_ptr->f_ptr->flags.has(TerrainCharacteristics::ENTRANCE)) {
590         eg_ptr->s2 = _("の入口", "");
591     }
592 #ifdef JP
593 #else
594     else if (eg_ptr->f_ptr->flags.has(TerrainCharacteristics::FLOOR) || eg_ptr->f_ptr->flags.has(TerrainCharacteristics::TOWN) || eg_ptr->f_ptr->flags.has(TerrainCharacteristics::SHALLOW) || eg_ptr->f_ptr->flags.has(TerrainCharacteristics::DEEP)) {
595         eg_ptr->s3 = "";
596     } else {
597         eg_ptr->s3 = (is_a_vowel(eg_ptr->name[0])) ? "an " : "a ";
598     }
599 #endif
600
601     describe_grid_monster_all(eg_ptr);
602     prt(eg_ptr->out_val, 0, 0);
603     move_cursor_relative(y, x);
604     eg_ptr->query = inkey();
605     if ((eg_ptr->query != '\r') && (eg_ptr->query != '\n')) {
606         return eg_ptr->query;
607     }
608
609     return 0;
610 }