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"
51 constexpr short CONTINUOUS_DESCRIPTION = 256;
53 class GridExamination {
55 GridExamination(FloorType &floor, POSITION y, POSITION x, target_type mode, concptr info);
66 char out_val[MAX_NLEN + 80]{};
67 OBJECT_IDX floor_list[23]{};
68 ITEM_NUMBER floor_num = 0;
71 OBJECT_IDX next_o_idx = 0;
73 TerrainType *terrain_ptr = nullptr;
74 std::string name = "";
77 GridExamination::GridExamination(FloorType &floor, POSITION y, POSITION x, target_type mode, concptr info)
83 this->g_ptr = &floor.grid_array[y][x];
84 this->m_ptr = &floor.m_list[this->g_ptr->m_idx];
89 bool show_gold_on_floor = false;
92 * Evaluate number of kill needed to gain level
94 static std::string evaluate_monster_exp(PlayerType *player_ptr, MonsterEntity *m_ptr)
96 MonsterRaceInfo *ap_r_ptr = &m_ptr->get_appearance_monrace();
97 if ((player_ptr->lev >= PY_MAX_LEVEL) || PlayerRace(player_ptr).equals(PlayerRaceType::ANDROID)) {
101 if (!ap_r_ptr->r_tkills || m_ptr->mflag2.has(MonsterConstantFlagType::KAGE)) {
102 if (!w_ptr->wizard) {
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));
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);
115 s64b_sub(&exp_adv, &exp_adv_frac, player_ptr->exp, player_ptr->exp_frac);
117 s64b_add(&exp_adv, &exp_adv_frac, exp_mon, exp_mon_frac);
118 s64b_sub(&exp_adv, &exp_adv_frac, 0, 1);
120 s64b_div(&exp_adv, &exp_adv_frac, exp_mon, exp_mon_frac);
122 auto num = std::min<uint>(999, exp_adv_frac);
123 return format("%03ld", (long int)num);
126 static void describe_scan_result(PlayerType *player_ptr, GridExamination *ge_ptr)
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,");
138 static void describe_target(PlayerType *player_ptr, GridExamination *ge_ptr)
140 if (!player_ptr->is_located_at({ ge_ptr->y, ge_ptr->x })) {
141 ge_ptr->s1 = _("ターゲット:", "Target:");
150 ge_ptr->s1 = "You are ";
155 static ProcessResult describe_hallucinated_target(PlayerType *player_ptr, GridExamination *ge_ptr)
157 if (!player_ptr->effects()->hallucination()->is_hallucinated()) {
158 return ProcessResult::PROCESS_CONTINUE;
161 concptr name = _("何か奇妙な物", "something strange");
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);
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);
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;
174 return ProcessResult::PROCESS_FALSE;
177 static bool describe_grid_lore(PlayerType *player_ptr, GridExamination *ge_ptr)
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();
184 return ge_ptr->query != 'r';
187 static void describe_grid_monster(PlayerType *player_ptr, GridExamination *ge_ptr)
190 const auto m_name = monster_desc(player_ptr, ge_ptr->m_ptr, MD_INDEF_VISIBLE);
193 if (describe_grid_lore(player_ptr, ge_ptr)) {
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);
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);
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);
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') {
221 static void describe_monster_person(GridExamination *ge_ptr)
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 ");
233 ge_ptr->s3 = "持っている";
235 ge_ptr->s2 = "carrying ";
239 static short describe_monster_item(PlayerType *player_ptr, GridExamination *ge_ptr)
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);
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);
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);
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;
256 if ((ge_ptr->query == ' ') && !(ge_ptr->mode & TARGET_LOOK)) {
257 return ge_ptr->query;
260 ge_ptr->s2 = _("をまた", "also carrying ");
263 return CONTINUOUS_DESCRIPTION;
266 static bool within_char_util(const short input)
268 return (input > -127) && (input < 128);
271 static short describe_grid(PlayerType *player_ptr, GridExamination *ge_ptr)
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;
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;
286 if ((ge_ptr->query == ' ') && !(ge_ptr->mode & TARGET_LOOK)) {
287 return ge_ptr->query;
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;
302 return CONTINUOUS_DESCRIPTION;
305 static short describe_footing(PlayerType *player_ptr, GridExamination *ge_ptr)
307 if (ge_ptr->floor_num != 1) {
308 return CONTINUOUS_DESCRIPTION;
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);
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);
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);
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;
324 static short describe_footing_items(GridExamination *ge_ptr)
326 if (!ge_ptr->boring) {
327 return CONTINUOUS_DESCRIPTION;
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);
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);
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;
342 return CONTINUOUS_DESCRIPTION;
345 static char describe_footing_many_items(PlayerType *player_ptr, GridExamination *ge_ptr, int *min_width)
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;
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);
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);
357 prt(ge_ptr->out_val, 0, 0);
358 ge_ptr->query = inkey();
360 if (ge_ptr->query != '\n' && ge_ptr->query != '\r') {
361 return ge_ptr->query;
364 if (ge_ptr->g_ptr->o_idx_list.size() < 2) {
368 ge_ptr->g_ptr->o_idx_list.rotate(player_ptr->current_floor_ptr);
370 // ターゲットしている床の座標を渡す必要があるので、window_stuff経由ではなく直接呼び出す
371 fix_floor_item_list(player_ptr, { ge_ptr->y, ge_ptr->x });
375 static short loop_describing_grid(PlayerType *player_ptr, GridExamination *ge_ptr)
377 if (ge_ptr->floor_num == 0) {
378 return CONTINUOUS_DESCRIPTION;
383 const auto footing_description = describe_footing(player_ptr, ge_ptr);
384 if (within_char_util(footing_description)) {
385 return (char)footing_description;
388 const auto footing_descriptions = describe_footing_items(ge_ptr);
389 if (within_char_util(footing_descriptions)) {
390 return (char)footing_descriptions;
393 return describe_footing_many_items(player_ptr, ge_ptr, &min_width);
397 static short describe_footing_sight(PlayerType *player_ptr, GridExamination *ge_ptr, ItemEntity *o_ptr)
399 if (o_ptr->marked.has_not(OmType::FOUND)) {
400 return CONTINUOUS_DESCRIPTION;
403 ge_ptr->boring = false;
404 const auto item_name = describe_flavor(player_ptr, o_ptr, 0);
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);
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);
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;
417 if ((ge_ptr->query == ' ') && !(ge_ptr->mode & TARGET_LOOK)) {
418 return ge_ptr->query;
421 ge_ptr->s1 = _("それは", "It is ");
422 if (o_ptr->number != 1) {
423 ge_ptr->s1 = _("それらは", "They are ");
432 return CONTINUOUS_DESCRIPTION;
435 static int16_t sweep_footing_items(PlayerType *player_ptr, GridExamination *ge_ptr)
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)) {
445 return CONTINUOUS_DESCRIPTION;
448 static std::string decide_target_floor(PlayerType *player_ptr, GridExamination *ge_ptr)
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';
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);
469 if (ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::BLDG) && !floor_ptr->inside_arena) {
470 return buildings[ge_ptr->terrain_ptr->subtype].name;
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);
478 if (ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::TOWN)) {
479 return towns_info[ge_ptr->g_ptr->special].name;
482 if (player_ptr->wild_mode && (ge_ptr->feat == feat_floor)) {
483 return _("道", "road");
486 return ge_ptr->terrain_ptr->name;
489 static void describe_grid_monster_all(GridExamination *ge_ptr)
491 if (!w_ptr->wizard) {
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);
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);
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);
504 f_idx_str = std::to_string(ge_ptr->g_ptr->feat);
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]);
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);
518 * @brief xまたはlで指定したグリッドにあるアイテムやモンスターの説明を記述する
519 * @param player_ptr プレイヤーへの参照ポインタ
520 * @param y 指定グリッドのY座標
521 * @param x 指定グリッドのX座標
522 * @param mode x (KILL)かl (LOOK)
527 char examine_grid(PlayerType *player_ptr, const POSITION y, const POSITION x, target_type mode, concptr info)
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:
537 case ProcessResult::PROCESS_TRUE:
538 return ge_ptr->query;
543 const auto description_grid = describe_grid(player_ptr, ge_ptr);
544 if (within_char_util(description_grid)) {
545 return (char)description_grid;
548 const auto loop_description = loop_describing_grid(player_ptr, ge_ptr);
549 if (within_char_util(loop_description)) {
550 return (char)loop_description;
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;
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;
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;
569 * グローバル変数への代入をここで行っているので動かしたくない
570 * 安全を確保できたら構造体から外すことも検討する
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 ");
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);
585 ge_ptr->s2 = _("の入口", "");
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) {
597 ge_ptr->s3 = (is_a_vowel(ge_ptr->name[0])) ? "an " : "a ";
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;