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 "monster/monster-util.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"
52 constexpr short CONTINUOUS_DESCRIPTION = 256;
54 class GridExamination {
56 GridExamination(FloorType &floor, POSITION y, POSITION x, target_type mode, concptr info);
67 char out_val[MAX_NLEN + 80]{};
68 OBJECT_IDX floor_list[23]{};
69 ITEM_NUMBER floor_num = 0;
72 OBJECT_IDX next_o_idx = 0;
74 TerrainType *terrain_ptr = nullptr;
75 std::string name = "";
78 GridExamination::GridExamination(FloorType &floor, POSITION y, POSITION x, target_type mode, concptr info)
84 this->g_ptr = &floor.grid_array[y][x];
85 this->m_ptr = &floor.m_list[this->g_ptr->m_idx];
90 bool show_gold_on_floor = false;
93 * Evaluate number of kill needed to gain level
95 static std::string evaluate_monster_exp(PlayerType *player_ptr, MonsterEntity *m_ptr)
97 MonsterRaceInfo *ap_r_ptr = &m_ptr->get_appearance_monrace();
98 if ((player_ptr->lev >= PY_MAX_LEVEL) || PlayerRace(player_ptr).equals(PlayerRaceType::ANDROID)) {
102 if (!ap_r_ptr->r_tkills || m_ptr->mflag2.has(MonsterConstantFlagType::KAGE)) {
103 if (!w_ptr->wizard) {
108 int32_t exp_mon = ap_r_ptr->mexp * ap_r_ptr->level;
109 uint32_t exp_mon_frac = 0;
110 s64b_div(&exp_mon, &exp_mon_frac, 0, (player_ptr->max_plv + 2));
112 int32_t exp_adv = player_exp[player_ptr->lev - 1] * player_ptr->expfact;
113 uint32_t exp_adv_frac = 0;
114 s64b_div(&exp_adv, &exp_adv_frac, 0, 100);
116 s64b_sub(&exp_adv, &exp_adv_frac, player_ptr->exp, player_ptr->exp_frac);
118 s64b_add(&exp_adv, &exp_adv_frac, exp_mon, exp_mon_frac);
119 s64b_sub(&exp_adv, &exp_adv_frac, 0, 1);
121 s64b_div(&exp_adv, &exp_adv_frac, exp_mon, exp_mon_frac);
123 auto num = std::min<uint>(999, exp_adv_frac);
124 return format("%03ld", (long int)num);
127 static void describe_scan_result(PlayerType *player_ptr, GridExamination *ge_ptr)
133 ge_ptr->floor_num = scan_floor_items(player_ptr, ge_ptr->floor_list, ge_ptr->y, ge_ptr->x, SCAN_FLOOR_ONLY_MARKED, AllMatchItemTester());
134 if (ge_ptr->floor_num > 0) {
135 ge_ptr->x_info = _("x物 ", "x,");
139 static void describe_target(PlayerType *player_ptr, GridExamination *ge_ptr)
141 if (!player_ptr->is_located_at({ ge_ptr->y, ge_ptr->x })) {
142 ge_ptr->s1 = _("ターゲット:", "Target:");
151 ge_ptr->s1 = "You are ";
156 static ProcessResult describe_hallucinated_target(PlayerType *player_ptr, GridExamination *ge_ptr)
158 if (!player_ptr->effects()->hallucination()->is_hallucinated()) {
159 return ProcessResult::PROCESS_CONTINUE;
162 concptr name = _("何か奇妙な物", "something strange");
164 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);
166 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);
168 prt(ge_ptr->out_val, 0, 0);
169 move_cursor_relative(ge_ptr->y, ge_ptr->x);
170 ge_ptr->query = inkey();
171 if ((ge_ptr->query != '\r') && (ge_ptr->query != '\n')) {
172 return ProcessResult::PROCESS_TRUE;
175 return ProcessResult::PROCESS_FALSE;
178 static bool describe_grid_lore(PlayerType *player_ptr, GridExamination *ge_ptr)
181 screen_roff(player_ptr, ge_ptr->m_ptr->ap_r_idx, MONSTER_LORE_NORMAL);
182 term_addstr(-1, TERM_WHITE, format(_(" [r思 %s%s]", " [r,%s%s]"), ge_ptr->x_info, ge_ptr->info));
183 ge_ptr->query = inkey();
185 return ge_ptr->query != 'r';
188 static void describe_grid_monster(PlayerType *player_ptr, GridExamination *ge_ptr)
191 const auto m_name = monster_desc(player_ptr, ge_ptr->m_ptr, MD_INDEF_VISIBLE);
194 if (describe_grid_lore(player_ptr, ge_ptr)) {
202 std::string acount = evaluate_monster_exp(player_ptr, ge_ptr->m_ptr);
203 const auto mon_desc = look_mon_desc(ge_ptr->m_ptr, 0x01);
205 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,
206 ge_ptr->x_info, ge_ptr->info);
208 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(),
209 ge_ptr->x_info, ge_ptr->info);
211 prt(ge_ptr->out_val, 0, 0);
212 move_cursor_relative(ge_ptr->y, ge_ptr->x);
213 ge_ptr->query = inkey();
214 if (ge_ptr->query != 'r') {
222 static void describe_monster_person(GridExamination *ge_ptr)
224 const auto &monrace = ge_ptr->m_ptr->get_appearance_monrace();
225 ge_ptr->s1 = _("それは", "It is ");
226 if (monrace.sex == MonsterSex::FEMALE) {
227 ge_ptr->s1 = _("彼女は", "She is ");
228 } else if (monrace.sex == MonsterSex::MALE) {
229 ge_ptr->s1 = _("彼は", "He is ");
234 ge_ptr->s3 = "持っている";
236 ge_ptr->s2 = "carrying ";
240 static short describe_monster_item(PlayerType *player_ptr, GridExamination *ge_ptr)
242 for (const auto this_o_idx : ge_ptr->m_ptr->hold_o_idx_list) {
243 auto *o_ptr = &player_ptr->current_floor_ptr->o_list[this_o_idx];
244 const auto item_name = describe_flavor(player_ptr, o_ptr, 0);
246 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);
248 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);
250 prt(ge_ptr->out_val, 0, 0);
251 move_cursor_relative(ge_ptr->y, ge_ptr->x);
252 ge_ptr->query = inkey();
253 if ((ge_ptr->query != '\r') && (ge_ptr->query != '\n') && (ge_ptr->query != ' ') && (ge_ptr->query != 'x')) {
254 return ge_ptr->query;
257 if ((ge_ptr->query == ' ') && !(ge_ptr->mode & TARGET_LOOK)) {
258 return ge_ptr->query;
261 ge_ptr->s2 = _("をまた", "also carrying ");
264 return CONTINUOUS_DESCRIPTION;
267 static bool within_char_util(const short input)
269 return (input > -127) && (input < 128);
272 static short describe_grid(PlayerType *player_ptr, GridExamination *ge_ptr)
274 if (!is_monster(ge_ptr->g_ptr->m_idx) || !player_ptr->current_floor_ptr->m_list[ge_ptr->g_ptr->m_idx].ml) {
275 return CONTINUOUS_DESCRIPTION;
278 ge_ptr->boring = false;
279 monster_race_track(player_ptr, ge_ptr->m_ptr->ap_r_idx);
280 health_track(player_ptr, ge_ptr->g_ptr->m_idx);
281 handle_stuff(player_ptr);
282 describe_grid_monster(player_ptr, ge_ptr);
283 if ((ge_ptr->query != '\r') && (ge_ptr->query != '\n') && (ge_ptr->query != ' ') && (ge_ptr->query != 'x')) {
284 return ge_ptr->query;
287 if ((ge_ptr->query == ' ') && !(ge_ptr->mode & TARGET_LOOK)) {
288 return ge_ptr->query;
291 describe_monster_person(ge_ptr);
292 const auto monster_item_description = describe_monster_item(player_ptr, ge_ptr);
293 if (within_char_util(monster_item_description)) {
294 return (char)monster_item_description;
303 return CONTINUOUS_DESCRIPTION;
306 static short describe_footing(PlayerType *player_ptr, GridExamination *ge_ptr)
308 if (ge_ptr->floor_num != 1) {
309 return CONTINUOUS_DESCRIPTION;
312 auto *o_ptr = &player_ptr->current_floor_ptr->o_list[ge_ptr->floor_list[0]];
313 const auto item_name = describe_flavor(player_ptr, o_ptr, 0);
315 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);
317 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);
319 prt(ge_ptr->out_val, 0, 0);
320 move_cursor_relative(ge_ptr->y, ge_ptr->x);
321 ge_ptr->query = inkey();
322 return ge_ptr->query;
325 static short describe_footing_items(GridExamination *ge_ptr)
327 if (!ge_ptr->boring) {
328 return CONTINUOUS_DESCRIPTION;
332 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);
334 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);
336 prt(ge_ptr->out_val, 0, 0);
337 move_cursor_relative(ge_ptr->y, ge_ptr->x);
338 ge_ptr->query = inkey();
339 if (ge_ptr->query != 'x' && ge_ptr->query != ' ') {
340 return ge_ptr->query;
343 return CONTINUOUS_DESCRIPTION;
346 static char describe_footing_many_items(PlayerType *player_ptr, GridExamination *ge_ptr, int *min_width)
350 show_gold_on_floor = true;
351 (void)show_floor_items(player_ptr, 0, ge_ptr->y, ge_ptr->x, min_width, AllMatchItemTester());
352 show_gold_on_floor = false;
354 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);
356 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);
358 prt(ge_ptr->out_val, 0, 0);
359 ge_ptr->query = inkey();
361 if (ge_ptr->query != '\n' && ge_ptr->query != '\r') {
362 return ge_ptr->query;
365 if (ge_ptr->g_ptr->o_idx_list.size() < 2) {
369 ge_ptr->g_ptr->o_idx_list.rotate(player_ptr->current_floor_ptr);
371 // ターゲットしている床の座標を渡す必要があるので、window_stuff経由ではなく直接呼び出す
372 fix_floor_item_list(player_ptr, { ge_ptr->y, ge_ptr->x });
376 static short loop_describing_grid(PlayerType *player_ptr, GridExamination *ge_ptr)
378 if (ge_ptr->floor_num == 0) {
379 return CONTINUOUS_DESCRIPTION;
384 const auto footing_description = describe_footing(player_ptr, ge_ptr);
385 if (within_char_util(footing_description)) {
386 return (char)footing_description;
389 const auto footing_descriptions = describe_footing_items(ge_ptr);
390 if (within_char_util(footing_descriptions)) {
391 return (char)footing_descriptions;
394 return describe_footing_many_items(player_ptr, ge_ptr, &min_width);
398 static short describe_footing_sight(PlayerType *player_ptr, GridExamination *ge_ptr, ItemEntity *o_ptr)
400 if (o_ptr->marked.has_not(OmType::FOUND)) {
401 return CONTINUOUS_DESCRIPTION;
404 ge_ptr->boring = false;
405 const auto item_name = describe_flavor(player_ptr, o_ptr, 0);
407 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);
409 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);
411 prt(ge_ptr->out_val, 0, 0);
412 move_cursor_relative(ge_ptr->y, ge_ptr->x);
413 ge_ptr->query = inkey();
414 if ((ge_ptr->query != '\r') && (ge_ptr->query != '\n') && (ge_ptr->query != ' ') && (ge_ptr->query != 'x')) {
415 return ge_ptr->query;
418 if ((ge_ptr->query == ' ') && !(ge_ptr->mode & TARGET_LOOK)) {
419 return ge_ptr->query;
422 ge_ptr->s1 = _("それは", "It is ");
423 if (o_ptr->number != 1) {
424 ge_ptr->s1 = _("それらは", "They are ");
433 return CONTINUOUS_DESCRIPTION;
436 static int16_t sweep_footing_items(PlayerType *player_ptr, GridExamination *ge_ptr)
438 for (const auto this_o_idx : ge_ptr->g_ptr->o_idx_list) {
439 auto *o_ptr = &player_ptr->current_floor_ptr->o_list[this_o_idx];
440 const auto ret = describe_footing_sight(player_ptr, ge_ptr, o_ptr);
441 if (within_char_util(ret)) {
446 return CONTINUOUS_DESCRIPTION;
449 static std::string decide_target_floor(PlayerType *player_ptr, GridExamination *ge_ptr)
451 auto *floor_ptr = player_ptr->current_floor_ptr;
452 if (ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::QUEST_ENTER)) {
453 QuestId old_quest = floor_ptr->quest_number;
454 const auto &quest_list = QuestList::get_instance();
455 const QuestId number = i2enum<QuestId>(ge_ptr->g_ptr->special);
456 const auto *q_ptr = &quest_list[number];
457 std::string_view msg(_("クエスト「%s」(%d階相当)", "the entrance to the quest '%s'(level %d)"));
458 for (int j = 0; j < 10; j++) {
459 quest_text[j][0] = '\0';
463 floor_ptr->quest_number = number;
464 init_flags = INIT_NAME_ONLY;
465 parse_fixed_map(player_ptr, QUEST_DEFINITION_LIST, 0, 0, 0, 0);
466 floor_ptr->quest_number = old_quest;
467 return format(msg.data(), q_ptr->name.data(), q_ptr->level);
470 if (ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::BLDG) && !floor_ptr->inside_arena) {
471 return buildings[ge_ptr->terrain_ptr->subtype].name;
474 if (ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::ENTRANCE)) {
475 const auto &dungeon = dungeons_info[ge_ptr->g_ptr->special];
476 return format(_("%s(%d階相当)", "%s(level %d)"), dungeon.text.data(), dungeon.mindepth);
479 if (ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::TOWN)) {
480 return towns_info[ge_ptr->g_ptr->special].name;
483 if (player_ptr->wild_mode && (ge_ptr->feat == feat_floor)) {
484 return _("道", "road");
487 return ge_ptr->terrain_ptr->name;
490 static void describe_grid_monster_all(GridExamination *ge_ptr)
492 if (!w_ptr->wizard) {
494 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);
496 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);
501 std::string f_idx_str;
502 if (ge_ptr->g_ptr->mimic) {
503 f_idx_str = format("%d/%d", ge_ptr->g_ptr->feat, ge_ptr->g_ptr->mimic);
505 f_idx_str = std::to_string(ge_ptr->g_ptr->feat);
509 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,
510 (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,
511 (int)ge_ptr->x, travel.cost[ge_ptr->y][ge_ptr->x]);
513 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,
514 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);
519 * @brief xまたはlで指定したグリッドにあるアイテムやモンスターの説明を記述する
520 * @param player_ptr プレイヤーへの参照ポインタ
521 * @param y 指定グリッドのY座標
522 * @param x 指定グリッドのX座標
523 * @param mode x (KILL)かl (LOOK)
528 char examine_grid(PlayerType *player_ptr, const POSITION y, const POSITION x, target_type mode, concptr info)
530 GridExamination tmp_eg(*player_ptr->current_floor_ptr, y, x, mode, info);
531 GridExamination *ge_ptr = &tmp_eg;
532 describe_scan_result(player_ptr, ge_ptr);
533 describe_target(player_ptr, ge_ptr);
534 ProcessResult next_target = describe_hallucinated_target(player_ptr, ge_ptr);
535 switch (next_target) {
536 case ProcessResult::PROCESS_FALSE:
538 case ProcessResult::PROCESS_TRUE:
539 return ge_ptr->query;
544 const auto description_grid = describe_grid(player_ptr, ge_ptr);
545 if (within_char_util(description_grid)) {
546 return (char)description_grid;
549 const auto loop_description = loop_describing_grid(player_ptr, ge_ptr);
550 if (within_char_util(loop_description)) {
551 return (char)loop_description;
554 const auto footing_items_description = sweep_footing_items(player_ptr, ge_ptr);
555 if (within_char_util(footing_items_description)) {
556 return (char)footing_items_description;
559 ge_ptr->feat = ge_ptr->g_ptr->get_feat_mimic();
560 if (!ge_ptr->g_ptr->is_mark() && !player_can_see_bold(player_ptr, y, x)) {
561 ge_ptr->feat = feat_none;
564 ge_ptr->terrain_ptr = &TerrainList::get_instance()[ge_ptr->feat];
565 if (!ge_ptr->boring && ge_ptr->terrain_ptr->flags.has_not(TerrainCharacteristics::REMEMBER)) {
566 return (ge_ptr->query != '\r') && (ge_ptr->query != '\n') ? ge_ptr->query : 0;
570 * グローバル変数への代入をここで行っているので動かしたくない
571 * 安全を確保できたら構造体から外すことも検討する
573 ge_ptr->name = decide_target_floor(player_ptr, ge_ptr);
574 auto is_in = ge_ptr->terrain_ptr->flags.has_none_of({ TerrainCharacteristics::MOVE, TerrainCharacteristics::CAN_FLY });
575 is_in |= ge_ptr->terrain_ptr->flags.has_none_of({ TerrainCharacteristics::LOS, TerrainCharacteristics::TREE });
576 is_in |= ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::TOWN);
577 if (*ge_ptr->s2 && is_in) {
578 ge_ptr->s2 = _("の中", "in ");
581 auto is_entrance = ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::STORE);
582 is_entrance |= ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::QUEST_ENTER);
583 is_entrance |= ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::BLDG) && !player_ptr->current_floor_ptr->inside_arena;
584 is_entrance |= ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::ENTRANCE);
586 ge_ptr->s2 = _("の入口", "");
591 auto is_normal_terrain = ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::FLOOR);
592 is_normal_terrain |= ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::TOWN);
593 is_normal_terrain |= ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::SHALLOW);
594 is_normal_terrain |= ge_ptr->terrain_ptr->flags.has(TerrainCharacteristics::DEEP);
595 if (is_normal_terrain) {
598 ge_ptr->s3 = (is_a_vowel(ge_ptr->name[0])) ? "an " : "a ";
603 describe_grid_monster_all(ge_ptr);
604 prt(ge_ptr->out_val, 0, 0);
605 move_cursor_relative(y, x);
606 ge_ptr->query = inkey();
607 if ((ge_ptr->query != '\r') && (ge_ptr->query != '\n')) {
608 return ge_ptr->query;