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/monster-flag-types.h"
11 #include "monster/monster-info.h"
12 #include "monster/monster-status.h"
13 #include "monster/monster-update.h"
14 #include "object/object-mark-types.h"
15 #include "object/tval-types.h"
16 #include "realm/realm-song-numbers.h"
17 #include "realm/realm-song.h"
18 #include "spell-realm/spells-song.h"
19 #include "system/dungeon-info.h"
20 #include "system/floor-type-definition.h"
21 #include "system/grid-type-definition.h"
22 #include "system/item-entity.h"
23 #include "system/monster-race-info.h"
24 #include "system/player-type-definition.h"
25 #include "system/redrawing-flags-updater.h"
26 #include "system/terrain-type-definition.h"
27 #include "tracking/lore-tracker.h"
28 #include "util/string-processor.h"
29 #include "view/display-messages.h"
32 * @brief プレイヤー周辺の地形を感知する
33 * @param player_ptr プレイヤーへの参照ポインタ
36 * @param known 地形から危険フラグを外すならTRUE
37 * @return 効力があった場合TRUEを返す
39 static bool detect_feat_flag(PlayerType *player_ptr, POSITION range, TerrainCharacteristics flag, bool known)
41 auto &floor = *player_ptr->current_floor_ptr;
42 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
47 for (POSITION y = 1; y < floor.height - 1; y++) {
48 for (POSITION x = 1; x <= floor.width - 1; x++) {
49 int dist = distance(player_ptr->y, player_ptr->x, y, x);
54 auto *g_ptr = &floor.grid_array[y][x];
55 if (flag == TerrainCharacteristics::TRAP) {
56 /* Mark as detected */
57 if (dist <= range && known) {
58 if (dist <= range - 1) {
59 g_ptr->info |= (CAVE_IN_DETECT);
62 g_ptr->info &= ~(CAVE_UNSAFE);
64 lite_spot(player_ptr, y, x);
68 if (g_ptr->cave_has_flag(flag)) {
69 disclose_grid(player_ptr, y, x);
70 g_ptr->info |= (CAVE_MARK);
71 lite_spot(player_ptr, y, x);
81 * @brief プレイヤー周辺のトラップを感知する / Detect all traps on current panel
82 * @param player_ptr プレイヤーへの参照ポインタ
84 * @param known 感知外範囲を超える警告フラグを立てる場合TRUEを返す
85 * @return 効力があった場合TRUEを返す
87 * 吟遊詩人による感知についてはFALSEを返す
89 bool detect_traps(PlayerType *player_ptr, POSITION range, bool known)
91 bool detect = detect_feat_flag(player_ptr, range, TerrainCharacteristics::TRAP, known);
92 if (!known && detect) {
93 detect_feat_flag(player_ptr, range, TerrainCharacteristics::TRAP, true);
96 if (known || detect) {
97 player_ptr->dtrap = true;
100 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 0) {
105 msg_print(_("トラップの存在を感じとった!", "You sense the presence of traps!"));
112 * @brief プレイヤー周辺のドアを感知する / Detect all doors on current panel
113 * @param player_ptr プレイヤーへの参照ポインタ
115 * @return 効力があった場合TRUEを返す
117 bool detect_doors(PlayerType *player_ptr, POSITION range)
119 bool detect = detect_feat_flag(player_ptr, range, TerrainCharacteristics::DOOR, true);
121 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 0) {
125 msg_print(_("ドアの存在を感じとった!", "You sense the presence of doors!"));
132 * @brief プレイヤー周辺の階段を感知する / Detect all stairs on current panel
133 * @param player_ptr プレイヤーへの参照ポインタ
135 * @return 効力があった場合TRUEを返す
137 bool detect_stairs(PlayerType *player_ptr, POSITION range)
139 bool detect = detect_feat_flag(player_ptr, range, TerrainCharacteristics::STAIRS, true);
141 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 0) {
145 msg_print(_("階段の存在を感じとった!", "You sense the presence of stairs!"));
152 * @brief プレイヤー周辺の地形財宝を感知する / Detect any treasure on the current panel
153 * @param player_ptr プレイヤーへの参照ポインタ
155 * @return 効力があった場合TRUEを返す
157 bool detect_treasure(PlayerType *player_ptr, POSITION range)
159 bool detect = detect_feat_flag(player_ptr, range, TerrainCharacteristics::HAS_GOLD, true);
161 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 6) {
165 msg_print(_("埋蔵された財宝の存在を感じとった!", "You sense the presence of buried treasure!"));
171 * @brief プレイヤー周辺のアイテム財宝を感知する / Detect all "gold" objects on the current panel
172 * @param player_ptr プレイヤーへの参照ポインタ
174 * @return 効力があった場合TRUEを返す
176 bool detect_objects_gold(PlayerType *player_ptr, POSITION range)
178 auto &floor = *player_ptr->current_floor_ptr;
179 POSITION range2 = range;
180 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
187 for (OBJECT_IDX i = 1; i < floor.o_max; i++) {
188 auto *o_ptr = &floor.o_list[i];
190 if (!o_ptr->is_valid()) {
193 if (o_ptr->is_held_by_monster()) {
199 if (distance(player_ptr->y, player_ptr->x, y, x) > range2) {
203 if (o_ptr->bi_key.tval() == ItemKindType::GOLD) {
204 o_ptr->marked.set(OmType::FOUND);
205 lite_spot(player_ptr, y, x);
210 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 6) {
214 msg_print(_("財宝の存在を感じとった!", "You sense the presence of treasure!"));
217 if (detect_monsters_string(player_ptr, range, "$")) {
225 * @brief 通常のアイテムオブジェクトを感知する / Detect all "normal" objects on the current panel
226 * @param player_ptr プレイヤーへの参照ポインタ
228 * @return 効力があった場合TRUEを返す
230 bool detect_objects_normal(PlayerType *player_ptr, POSITION range)
232 auto &floor = *player_ptr->current_floor_ptr;
233 POSITION range2 = range;
234 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
239 for (OBJECT_IDX i = 1; i < floor.o_max; i++) {
240 auto *o_ptr = &floor.o_list[i];
242 if (!o_ptr->is_valid()) {
245 if (o_ptr->is_held_by_monster()) {
249 POSITION y = o_ptr->iy;
250 POSITION x = o_ptr->ix;
252 if (distance(player_ptr->y, player_ptr->x, y, x) > range2) {
256 if (o_ptr->bi_key.tval() != ItemKindType::GOLD) {
257 o_ptr->marked.set(OmType::FOUND);
258 lite_spot(player_ptr, y, x);
263 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 6) {
267 RedrawingFlagsUpdater::get_instance().set_flag(SubWindowRedrawingFlag::FOUND_ITEMS);
268 msg_print(_("アイテムの存在を感じとった!", "You sense the presence of objects!"));
271 if (detect_monsters_string(player_ptr, range, "!=?|/`")) {
278 static bool is_object_magically(const ItemKindType tval)
281 case ItemKindType::WHISTLE:
282 case ItemKindType::AMULET:
283 case ItemKindType::RING:
284 case ItemKindType::STAFF:
285 case ItemKindType::WAND:
286 case ItemKindType::ROD:
287 case ItemKindType::SCROLL:
288 case ItemKindType::POTION:
296 * @brief 魔法効果のあるのアイテムオブジェクトを感知する
297 * @param player_ptr プレイヤーへの参照ポインタ
299 * @return 1つ以上感知したか否か
301 bool detect_objects_magic(PlayerType *player_ptr, POSITION range)
303 auto &floor = *player_ptr->current_floor_ptr;
304 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
309 for (OBJECT_IDX i = 1; i < floor.o_max; i++) {
310 auto *o_ptr = &floor.o_list[i];
311 if (!o_ptr->is_valid() || o_ptr->is_held_by_monster()) {
317 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
321 auto has_bonus = o_ptr->to_a > 0;
322 has_bonus |= o_ptr->to_h + o_ptr->to_d > 0;
323 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) {
324 o_ptr->marked.set(OmType::FOUND);
325 lite_spot(player_ptr, y, x);
331 RedrawingFlagsUpdater::get_instance().set_flag(SubWindowRedrawingFlag::FOUND_ITEMS);
332 msg_print(_("魔法のアイテムの存在を感じとった!", "You sense the presence of magic objects!"));
339 * @brief 一般のモンスターを感知する / Detect all "normal" monsters on the current panel
340 * @param player_ptr プレイヤーへの参照ポインタ
342 * @return 効力があった場合TRUEを返す
344 bool detect_monsters_normal(PlayerType *player_ptr, POSITION range)
346 auto &floor = *player_ptr->current_floor_ptr;
347 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
352 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
353 auto *m_ptr = &floor.m_list[i];
354 auto *r_ptr = &m_ptr->get_monrace();
355 if (!m_ptr->is_valid()) {
359 POSITION y = m_ptr->fy;
360 POSITION x = m_ptr->fx;
361 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
365 if (r_ptr->misc_flags.has_not(MonsterMiscType::INVISIBLE) || player_ptr->see_inv) {
366 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
367 update_monster(player_ptr, i, false);
372 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 3) {
376 msg_print(_("モンスターの存在を感じとった!", "You sense the presence of monsters!"));
383 * @brief 不可視のモンスターを感知する / Detect all "invisible" monsters around the player
384 * @param player_ptr プレイヤーへの参照ポインタ
386 * @return 効力があった場合TRUEを返す
388 bool detect_monsters_invis(PlayerType *player_ptr, POSITION range)
390 auto &floor = *player_ptr->current_floor_ptr;
391 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
395 const auto &tracker = LoreTracker::get_instance();
396 auto &rfu = RedrawingFlagsUpdater::get_instance();
398 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
399 auto *m_ptr = &floor.m_list[i];
400 auto *r_ptr = &m_ptr->get_monrace();
402 if (!m_ptr->is_valid()) {
406 POSITION y = m_ptr->fy;
407 POSITION x = m_ptr->fx;
409 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
413 if (r_ptr->misc_flags.has(MonsterMiscType::INVISIBLE)) {
414 if (tracker.is_tracking(m_ptr->r_idx)) {
415 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
418 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
419 update_monster(player_ptr, i, false);
424 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 3) {
428 msg_print(_("透明な生物の存在を感じとった!", "You sense the presence of invisible creatures!"));
435 * @brief 邪悪なモンスターを感知する / Detect all "evil" monsters on current panel
436 * @param player_ptr プレイヤーへの参照ポインタ
438 * @return 効力があった場合TRUEを返す
440 bool detect_monsters_evil(PlayerType *player_ptr, POSITION range)
442 auto &floor = *player_ptr->current_floor_ptr;
443 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
447 const auto &tracker = LoreTracker::get_instance();
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 = &m_ptr->get_monrace();
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 (tracker.is_tracking(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 const auto &tracker = LoreTracker::get_instance();
499 auto &rfu = RedrawingFlagsUpdater::get_instance();
501 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
502 auto *m_ptr = &floor.m_list[i];
503 if (!m_ptr->is_valid()) {
507 POSITION y = m_ptr->fy;
508 POSITION x = m_ptr->fx;
509 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
513 if (!m_ptr->has_living_flag()) {
514 if (tracker.is_tracking(m_ptr->r_idx)) {
515 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
518 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
519 update_monster(player_ptr, i, false);
525 msg_print(_("自然でないモンスターの存在を感じた!", "You sense the presence of unnatural beings!"));
532 * @brief 精神のあるモンスターを感知する / Detect all monsters it has mind on current panel
533 * @param player_ptr プレイヤーへの参照ポインタ
535 * @return 効力があった場合TRUEを返す
537 bool detect_monsters_mind(PlayerType *player_ptr, POSITION range)
539 auto &floor = *player_ptr->current_floor_ptr;
540 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
544 const auto &tracker = LoreTracker::get_instance();
545 auto &rfu = RedrawingFlagsUpdater::get_instance();
547 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
548 auto *m_ptr = &floor.m_list[i];
549 auto *r_ptr = &m_ptr->get_monrace();
550 if (!m_ptr->is_valid()) {
554 POSITION y = m_ptr->fy;
555 POSITION x = m_ptr->fx;
557 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
561 if (r_ptr->misc_flags.has_not(MonsterMiscType::EMPTY_MIND)) {
562 if (tracker.is_tracking(m_ptr->r_idx)) {
563 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
566 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
567 update_monster(player_ptr, i, false);
573 msg_print(_("殺気を感じとった!", "You sense the presence of someone's mind!"));
580 * @brief 該当シンボルのモンスターを感知する / Detect all (string) monsters on current panel
581 * @param player_ptr プレイヤーへの参照ポインタ
583 * @param Match 対応シンボルの混じったモンスター文字列(複数指定化)
584 * @return 効力があった場合TRUEを返す
586 bool detect_monsters_string(PlayerType *player_ptr, POSITION range, concptr Match)
588 auto &floor = *player_ptr->current_floor_ptr;
589 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
593 const auto &tracker = LoreTracker::get_instance();
594 auto &rfu = RedrawingFlagsUpdater::get_instance();
596 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
597 auto *m_ptr = &floor.m_list[i];
598 const auto &monrace = m_ptr->get_monrace();
599 if (!m_ptr->is_valid()) {
603 POSITION y = m_ptr->fy;
604 POSITION x = m_ptr->fx;
606 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
610 if (angband_strchr(Match, monrace.symbol_definition.character)) {
611 if (tracker.is_tracking(m_ptr->r_idx)) {
612 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
615 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
616 update_monster(player_ptr, i, false);
621 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 3) {
625 msg_print(_("モンスターの存在を感じとった!", "You sense the presence of monsters!"));
632 * @brief 全感知処理 / Detect everything
633 * @param player_ptr プレイヤーへの参照ポインタ
635 * @return 効力があった場合TRUEを返す
637 bool detect_all(PlayerType *player_ptr, POSITION range)
640 if (detect_traps(player_ptr, range, true)) {
643 if (detect_doors(player_ptr, range)) {
646 if (detect_stairs(player_ptr, range)) {
649 if (detect_objects_gold(player_ptr, range)) {
652 if (detect_objects_normal(player_ptr, range)) {
655 if (detect_monsters_invis(player_ptr, range)) {
658 if (detect_monsters_normal(player_ptr, range)) {