1 #include "spell-kind/spells-detection.h"
2 #include "core/window-redrawer.h"
3 #include "dungeon/dungeon-flag-types.h"
4 #include "floor/cave.h"
5 #include "floor/floor-save-util.h"
6 #include "floor/geometry.h"
9 #include "monster-race/monster-race-hook.h"
10 #include "monster-race/monster-race.h"
11 #include "monster-race/race-flags2.h"
12 #include "monster-race/race-flags3.h"
13 #include "monster/monster-flag-types.h"
14 #include "monster/monster-info.h"
15 #include "monster/monster-status.h"
16 #include "monster/monster-update.h"
17 #include "object/object-mark-types.h"
18 #include "object/tval-types.h"
19 #include "realm/realm-song-numbers.h"
20 #include "realm/realm-song.h"
21 #include "spell-realm/spells-song.h"
22 #include "system/dungeon-info.h"
23 #include "system/floor-type-definition.h"
24 #include "system/grid-type-definition.h"
25 #include "system/item-entity.h"
26 #include "system/monster-race-info.h"
27 #include "system/player-type-definition.h"
28 #include "system/redrawing-flags-updater.h"
29 #include "system/terrain-type-definition.h"
30 #include "util/string-processor.h"
31 #include "view/display-messages.h"
34 * @brief プレイヤー周辺の地形を感知する
35 * @param player_ptr プレイヤーへの参照ポインタ
38 * @param known 地形から危険フラグを外すならTRUE
39 * @return 効力があった場合TRUEを返す
41 static bool detect_feat_flag(PlayerType *player_ptr, POSITION range, TerrainCharacteristics flag, bool known)
43 auto &floor = *player_ptr->current_floor_ptr;
44 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
49 for (POSITION y = 1; y < floor.height - 1; y++) {
50 for (POSITION x = 1; x <= floor.width - 1; x++) {
51 int dist = distance(player_ptr->y, player_ptr->x, y, x);
56 auto *g_ptr = &floor.grid_array[y][x];
57 if (flag == TerrainCharacteristics::TRAP) {
58 /* Mark as detected */
59 if (dist <= range && known) {
60 if (dist <= range - 1) {
61 g_ptr->info |= (CAVE_IN_DETECT);
64 g_ptr->info &= ~(CAVE_UNSAFE);
66 lite_spot(player_ptr, y, x);
70 if (g_ptr->cave_has_flag(flag)) {
71 disclose_grid(player_ptr, y, x);
72 g_ptr->info |= (CAVE_MARK);
73 lite_spot(player_ptr, y, x);
83 * @brief プレイヤー周辺のトラップを感知する / Detect all traps on current panel
84 * @param player_ptr プレイヤーへの参照ポインタ
86 * @param known 感知外範囲を超える警告フラグを立てる場合TRUEを返す
87 * @return 効力があった場合TRUEを返す
89 * 吟遊詩人による感知についてはFALSEを返す
91 bool detect_traps(PlayerType *player_ptr, POSITION range, bool known)
93 bool detect = detect_feat_flag(player_ptr, range, TerrainCharacteristics::TRAP, known);
94 if (!known && detect) {
95 detect_feat_flag(player_ptr, range, TerrainCharacteristics::TRAP, true);
98 if (known || detect) {
99 player_ptr->dtrap = true;
102 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 0) {
107 msg_print(_("トラップの存在を感じとった!", "You sense the presence of traps!"));
114 * @brief プレイヤー周辺のドアを感知する / Detect all doors on current panel
115 * @param player_ptr プレイヤーへの参照ポインタ
117 * @return 効力があった場合TRUEを返す
119 bool detect_doors(PlayerType *player_ptr, POSITION range)
121 bool detect = detect_feat_flag(player_ptr, range, TerrainCharacteristics::DOOR, true);
123 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 0) {
127 msg_print(_("ドアの存在を感じとった!", "You sense the presence of doors!"));
134 * @brief プレイヤー周辺の階段を感知する / Detect all stairs on current panel
135 * @param player_ptr プレイヤーへの参照ポインタ
137 * @return 効力があった場合TRUEを返す
139 bool detect_stairs(PlayerType *player_ptr, POSITION range)
141 bool detect = detect_feat_flag(player_ptr, range, TerrainCharacteristics::STAIRS, true);
143 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 0) {
147 msg_print(_("階段の存在を感じとった!", "You sense the presence of stairs!"));
154 * @brief プレイヤー周辺の地形財宝を感知する / Detect any treasure on the current panel
155 * @param player_ptr プレイヤーへの参照ポインタ
157 * @return 効力があった場合TRUEを返す
159 bool detect_treasure(PlayerType *player_ptr, POSITION range)
161 bool detect = detect_feat_flag(player_ptr, range, TerrainCharacteristics::HAS_GOLD, true);
163 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 6) {
167 msg_print(_("埋蔵された財宝の存在を感じとった!", "You sense the presence of buried treasure!"));
173 * @brief プレイヤー周辺のアイテム財宝を感知する / Detect all "gold" objects on the current panel
174 * @param player_ptr プレイヤーへの参照ポインタ
176 * @return 効力があった場合TRUEを返す
178 bool detect_objects_gold(PlayerType *player_ptr, POSITION range)
180 auto &floor = *player_ptr->current_floor_ptr;
181 POSITION range2 = range;
182 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
189 for (OBJECT_IDX i = 1; i < floor.o_max; i++) {
190 auto *o_ptr = &floor.o_list[i];
192 if (!o_ptr->is_valid()) {
195 if (o_ptr->is_held_by_monster()) {
201 if (distance(player_ptr->y, player_ptr->x, y, x) > range2) {
205 if (o_ptr->bi_key.tval() == ItemKindType::GOLD) {
206 o_ptr->marked.set(OmType::FOUND);
207 lite_spot(player_ptr, y, x);
212 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 6) {
216 msg_print(_("財宝の存在を感じとった!", "You sense the presence of treasure!"));
219 if (detect_monsters_string(player_ptr, range, "$")) {
227 * @brief 通常のアイテムオブジェクトを感知する / Detect all "normal" objects on the current panel
228 * @param player_ptr プレイヤーへの参照ポインタ
230 * @return 効力があった場合TRUEを返す
232 bool detect_objects_normal(PlayerType *player_ptr, POSITION range)
234 auto &floor = *player_ptr->current_floor_ptr;
235 POSITION range2 = range;
236 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
241 for (OBJECT_IDX i = 1; i < floor.o_max; i++) {
242 auto *o_ptr = &floor.o_list[i];
244 if (!o_ptr->is_valid()) {
247 if (o_ptr->is_held_by_monster()) {
251 POSITION y = o_ptr->iy;
252 POSITION x = o_ptr->ix;
254 if (distance(player_ptr->y, player_ptr->x, y, x) > range2) {
258 if (o_ptr->bi_key.tval() != ItemKindType::GOLD) {
259 o_ptr->marked.set(OmType::FOUND);
260 lite_spot(player_ptr, y, x);
265 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 6) {
269 RedrawingFlagsUpdater::get_instance().set_flag(SubWindowRedrawingFlag::FOUND_ITEMS);
270 msg_print(_("アイテムの存在を感じとった!", "You sense the presence of objects!"));
273 if (detect_monsters_string(player_ptr, range, "!=?|/`")) {
280 static bool is_object_magically(const ItemKindType tval)
283 case ItemKindType::WHISTLE:
284 case ItemKindType::AMULET:
285 case ItemKindType::RING:
286 case ItemKindType::STAFF:
287 case ItemKindType::WAND:
288 case ItemKindType::ROD:
289 case ItemKindType::SCROLL:
290 case ItemKindType::POTION:
298 * @brief 魔法効果のあるのアイテムオブジェクトを感知する
299 * @param player_ptr プレイヤーへの参照ポインタ
301 * @return 1つ以上感知したか否か
303 bool detect_objects_magic(PlayerType *player_ptr, POSITION range)
305 auto &floor = *player_ptr->current_floor_ptr;
306 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
311 for (OBJECT_IDX i = 1; i < floor.o_max; i++) {
312 auto *o_ptr = &floor.o_list[i];
313 if (!o_ptr->is_valid() || o_ptr->is_held_by_monster()) {
319 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
323 auto has_bonus = o_ptr->to_a > 0;
324 has_bonus |= o_ptr->to_h + o_ptr->to_d > 0;
325 if (o_ptr->is_fixed_or_random_artifact() || o_ptr->is_ego() || is_object_magically(o_ptr->bi_key.tval()) || o_ptr->is_spell_book() || has_bonus) {
326 o_ptr->marked.set(OmType::FOUND);
327 lite_spot(player_ptr, y, x);
333 RedrawingFlagsUpdater::get_instance().set_flag(SubWindowRedrawingFlag::FOUND_ITEMS);
334 msg_print(_("魔法のアイテムの存在を感じとった!", "You sense the presence of magic objects!"));
341 * @brief 一般のモンスターを感知する / Detect all "normal" monsters on the current panel
342 * @param player_ptr プレイヤーへの参照ポインタ
344 * @return 効力があった場合TRUEを返す
346 bool detect_monsters_normal(PlayerType *player_ptr, POSITION range)
348 auto &floor = *player_ptr->current_floor_ptr;
349 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
354 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
355 auto *m_ptr = &floor.m_list[i];
356 auto *r_ptr = &monraces_info[m_ptr->r_idx];
357 if (!m_ptr->is_valid()) {
361 POSITION y = m_ptr->fy;
362 POSITION x = m_ptr->fx;
363 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
367 if (!(r_ptr->flags2 & RF2_INVISIBLE) || player_ptr->see_inv) {
368 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
369 update_monster(player_ptr, i, false);
374 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 3) {
378 msg_print(_("モンスターの存在を感じとった!", "You sense the presence of monsters!"));
385 * @brief 不可視のモンスターを感知する / Detect all "invisible" monsters around the player
386 * @param player_ptr プレイヤーへの参照ポインタ
388 * @return 効力があった場合TRUEを返す
390 bool detect_monsters_invis(PlayerType *player_ptr, POSITION range)
392 auto &floor = *player_ptr->current_floor_ptr;
393 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
397 auto &rfu = RedrawingFlagsUpdater::get_instance();
399 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
400 auto *m_ptr = &floor.m_list[i];
401 auto *r_ptr = &monraces_info[m_ptr->r_idx];
403 if (!m_ptr->is_valid()) {
407 POSITION y = m_ptr->fy;
408 POSITION x = m_ptr->fx;
410 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
414 if (r_ptr->flags2 & RF2_INVISIBLE) {
415 if (player_ptr->monster_race_idx == m_ptr->r_idx) {
416 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
419 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
420 update_monster(player_ptr, i, false);
425 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 3) {
429 msg_print(_("透明な生物の存在を感じとった!", "You sense the presence of invisible creatures!"));
436 * @brief 邪悪なモンスターを感知する / Detect all "evil" monsters on current panel
437 * @param player_ptr プレイヤーへの参照ポインタ
439 * @return 効力があった場合TRUEを返す
441 bool detect_monsters_evil(PlayerType *player_ptr, POSITION range)
443 auto &floor = *player_ptr->current_floor_ptr;
444 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
448 auto &rfu = RedrawingFlagsUpdater::get_instance();
450 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
451 auto *m_ptr = &floor.m_list[i];
452 auto *r_ptr = &monraces_info[m_ptr->r_idx];
453 if (!m_ptr->is_valid()) {
457 POSITION y = m_ptr->fy;
458 POSITION x = m_ptr->fx;
460 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
464 if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
465 if (m_ptr->is_original_ap()) {
466 r_ptr->r_kind_flags.set(MonsterKindType::EVIL);
467 if (player_ptr->monster_race_idx == m_ptr->r_idx) {
468 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
472 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
473 update_monster(player_ptr, i, false);
479 msg_print(_("邪悪なる生物の存在を感じとった!", "You sense the presence of evil creatures!"));
486 * @brief 無生命のモンスターを感知する(アンデッド、悪魔系を含む) / Detect all "nonliving", "undead" or "demonic" monsters on current panel
487 * @param player_ptr プレイヤーへの参照ポインタ
489 * @return 効力があった場合TRUEを返す
491 bool detect_monsters_nonliving(PlayerType *player_ptr, POSITION range)
493 auto &floor = *player_ptr->current_floor_ptr;
494 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
498 auto &rfu = RedrawingFlagsUpdater::get_instance();
500 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
501 auto *m_ptr = &floor.m_list[i];
502 if (!m_ptr->is_valid()) {
506 POSITION y = m_ptr->fy;
507 POSITION x = m_ptr->fx;
508 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
512 if (!m_ptr->has_living_flag()) {
513 if (player_ptr->monster_race_idx == m_ptr->r_idx) {
514 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
517 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
518 update_monster(player_ptr, i, false);
524 msg_print(_("自然でないモンスターの存在を感じた!", "You sense the presence of unnatural beings!"));
531 * @brief 精神のあるモンスターを感知する / Detect all monsters it has mind on current panel
532 * @param player_ptr プレイヤーへの参照ポインタ
534 * @return 効力があった場合TRUEを返す
536 bool detect_monsters_mind(PlayerType *player_ptr, POSITION range)
538 auto &floor = *player_ptr->current_floor_ptr;
539 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
543 auto &rfu = RedrawingFlagsUpdater::get_instance();
545 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
546 auto *m_ptr = &floor.m_list[i];
547 auto *r_ptr = &monraces_info[m_ptr->r_idx];
548 if (!m_ptr->is_valid()) {
552 POSITION y = m_ptr->fy;
553 POSITION x = m_ptr->fx;
555 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
559 if (!(r_ptr->flags2 & RF2_EMPTY_MIND)) {
560 if (player_ptr->monster_race_idx == m_ptr->r_idx) {
561 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
564 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
565 update_monster(player_ptr, i, false);
571 msg_print(_("殺気を感じとった!", "You sense the presence of someone's mind!"));
578 * @brief 該当シンボルのモンスターを感知する / Detect all (string) monsters on current panel
579 * @param player_ptr プレイヤーへの参照ポインタ
581 * @param Match 対応シンボルの混じったモンスター文字列(複数指定化)
582 * @return 効力があった場合TRUEを返す
584 bool detect_monsters_string(PlayerType *player_ptr, POSITION range, concptr Match)
586 auto &floor = *player_ptr->current_floor_ptr;
587 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
591 auto &rfu = RedrawingFlagsUpdater::get_instance();
593 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
594 auto *m_ptr = &floor.m_list[i];
595 auto *r_ptr = &monraces_info[m_ptr->r_idx];
596 if (!m_ptr->is_valid()) {
600 POSITION y = m_ptr->fy;
601 POSITION x = m_ptr->fx;
603 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
607 if (angband_strchr(Match, r_ptr->d_char)) {
608 if (player_ptr->monster_race_idx == m_ptr->r_idx) {
609 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
612 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
613 update_monster(player_ptr, i, false);
618 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 3) {
622 msg_print(_("モンスターの存在を感じとった!", "You sense the presence of monsters!"));
629 * @brief 全感知処理 / Detect everything
630 * @param player_ptr プレイヤーへの参照ポインタ
632 * @return 効力があった場合TRUEを返す
634 bool detect_all(PlayerType *player_ptr, POSITION range)
637 if (detect_traps(player_ptr, range, true)) {
640 if (detect_doors(player_ptr, range)) {
643 if (detect_stairs(player_ptr, range)) {
646 if (detect_objects_gold(player_ptr, range)) {
649 if (detect_objects_normal(player_ptr, range)) {
652 if (detect_monsters_invis(player_ptr, range)) {
655 if (detect_monsters_normal(player_ptr, range)) {