OSDN Git Service

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