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/monster-flag-types.h"
12 #include "monster/monster-info.h"
13 #include "monster/monster-status.h"
14 #include "monster/monster-update.h"
15 #include "object/object-mark-types.h"
16 #include "object/tval-types.h"
17 #include "realm/realm-song-numbers.h"
18 #include "realm/realm-song.h"
19 #include "spell-realm/spells-song.h"
20 #include "system/dungeon-info.h"
21 #include "system/floor-type-definition.h"
22 #include "system/grid-type-definition.h"
23 #include "system/item-entity.h"
24 #include "system/monster-race-info.h"
25 #include "system/player-type-definition.h"
26 #include "system/redrawing-flags-updater.h"
27 #include "system/terrain-type-definition.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 auto &rfu = RedrawingFlagsUpdater::get_instance();
397 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
398 auto *m_ptr = &floor.m_list[i];
399 auto *r_ptr = &m_ptr->get_monrace();
401 if (!m_ptr->is_valid()) {
405 POSITION y = m_ptr->fy;
406 POSITION x = m_ptr->fx;
408 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
412 if (r_ptr->misc_flags.has(MonsterMiscType::INVISIBLE)) {
413 if (player_ptr->monster_race_idx == m_ptr->r_idx) {
414 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
417 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
418 update_monster(player_ptr, i, false);
423 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 3) {
427 msg_print(_("透明な生物の存在を感じとった!", "You sense the presence of invisible creatures!"));
434 * @brief 邪悪なモンスターを感知する / Detect all "evil" monsters on current panel
435 * @param player_ptr プレイヤーへの参照ポインタ
437 * @return 効力があった場合TRUEを返す
439 bool detect_monsters_evil(PlayerType *player_ptr, POSITION range)
441 auto &floor = *player_ptr->current_floor_ptr;
442 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
446 auto &rfu = RedrawingFlagsUpdater::get_instance();
448 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
449 auto *m_ptr = &floor.m_list[i];
450 auto *r_ptr = &m_ptr->get_monrace();
451 if (!m_ptr->is_valid()) {
455 POSITION y = m_ptr->fy;
456 POSITION x = m_ptr->fx;
458 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
462 if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
463 if (m_ptr->is_original_ap()) {
464 r_ptr->r_kind_flags.set(MonsterKindType::EVIL);
465 if (player_ptr->monster_race_idx == m_ptr->r_idx) {
466 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
470 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
471 update_monster(player_ptr, i, false);
477 msg_print(_("邪悪なる生物の存在を感じとった!", "You sense the presence of evil creatures!"));
484 * @brief 無生命のモンスターを感知する(アンデッド、悪魔系を含む) / Detect all "nonliving", "undead" or "demonic" monsters on current panel
485 * @param player_ptr プレイヤーへの参照ポインタ
487 * @return 効力があった場合TRUEを返す
489 bool detect_monsters_nonliving(PlayerType *player_ptr, POSITION range)
491 auto &floor = *player_ptr->current_floor_ptr;
492 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
496 auto &rfu = RedrawingFlagsUpdater::get_instance();
498 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
499 auto *m_ptr = &floor.m_list[i];
500 if (!m_ptr->is_valid()) {
504 POSITION y = m_ptr->fy;
505 POSITION x = m_ptr->fx;
506 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
510 if (!m_ptr->has_living_flag()) {
511 if (player_ptr->monster_race_idx == m_ptr->r_idx) {
512 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
515 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
516 update_monster(player_ptr, i, false);
522 msg_print(_("自然でないモンスターの存在を感じた!", "You sense the presence of unnatural beings!"));
529 * @brief 精神のあるモンスターを感知する / Detect all monsters it has mind on current panel
530 * @param player_ptr プレイヤーへの参照ポインタ
532 * @return 効力があった場合TRUEを返す
534 bool detect_monsters_mind(PlayerType *player_ptr, POSITION range)
536 auto &floor = *player_ptr->current_floor_ptr;
537 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
541 auto &rfu = RedrawingFlagsUpdater::get_instance();
543 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
544 auto *m_ptr = &floor.m_list[i];
545 auto *r_ptr = &m_ptr->get_monrace();
546 if (!m_ptr->is_valid()) {
550 POSITION y = m_ptr->fy;
551 POSITION x = m_ptr->fx;
553 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
557 if (r_ptr->misc_flags.has_not(MonsterMiscType::EMPTY_MIND)) {
558 if (player_ptr->monster_race_idx == m_ptr->r_idx) {
559 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
562 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
563 update_monster(player_ptr, i, false);
569 msg_print(_("殺気を感じとった!", "You sense the presence of someone's mind!"));
576 * @brief 該当シンボルのモンスターを感知する / Detect all (string) monsters on current panel
577 * @param player_ptr プレイヤーへの参照ポインタ
579 * @param Match 対応シンボルの混じったモンスター文字列(複数指定化)
580 * @return 効力があった場合TRUEを返す
582 bool detect_monsters_string(PlayerType *player_ptr, POSITION range, concptr Match)
584 auto &floor = *player_ptr->current_floor_ptr;
585 if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
589 auto &rfu = RedrawingFlagsUpdater::get_instance();
591 for (MONSTER_IDX i = 1; i < floor.m_max; i++) {
592 auto *m_ptr = &floor.m_list[i];
593 auto *r_ptr = &m_ptr->get_monrace();
594 if (!m_ptr->is_valid()) {
598 POSITION y = m_ptr->fy;
599 POSITION x = m_ptr->fx;
601 if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
605 if (angband_strchr(Match, r_ptr->d_char)) {
606 if (player_ptr->monster_race_idx == m_ptr->r_idx) {
607 rfu.set_flag(SubWindowRedrawingFlag::MONSTER_LORE);
610 m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
611 update_monster(player_ptr, i, false);
616 if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 3) {
620 msg_print(_("モンスターの存在を感じとった!", "You sense the presence of monsters!"));
627 * @brief 全感知処理 / Detect everything
628 * @param player_ptr プレイヤーへの参照ポインタ
630 * @return 効力があった場合TRUEを返す
632 bool detect_all(PlayerType *player_ptr, POSITION range)
635 if (detect_traps(player_ptr, range, true)) {
638 if (detect_doors(player_ptr, range)) {
641 if (detect_stairs(player_ptr, range)) {
644 if (detect_objects_gold(player_ptr, range)) {
647 if (detect_objects_normal(player_ptr, range)) {
650 if (detect_monsters_invis(player_ptr, range)) {
653 if (detect_monsters_normal(player_ptr, range)) {