1 #include "spell-kind/spells-detection.h"
2 #include "core/window-redrawer.h"
3 #include "dungeon/dungeon-flag-types.h"
4 #include "dungeon/dungeon.h"
5 #include "floor/cave.h"
6 #include "floor/geometry.h"
7 #include "floor/floor-save-util.h"
8 #include "grid/feature.h"
10 #include "grid/trap.h"
11 #include "monster-race/monster-race.h"
12 #include "monster-race/race-flags2.h"
13 #include "monster-race/race-flags3.h"
14 #include "monster-race/monster-race-hook.h"
15 #include "monster/monster-flag-types.h"
16 #include "monster/monster-info.h"
17 #include "monster/monster-status.h"
18 #include "monster/monster-update.h"
19 #include "object-hook/hook-checker.h"
20 #include "object-hook/hook-enchant.h"
21 #include "object/object-mark-types.h"
22 #include "object/tval-types.h"
23 #include "realm/realm-song-numbers.h"
24 #include "realm/realm-song.h"
25 #include "spell-realm/spells-song.h"
26 #include "system/floor-type-definition.h"
27 #include "system/monster-race-definition.h"
28 #include "system/object-type-definition.h"
29 #include "system/player-type-definition.h"
30 #include "util/string-processor.h"
31 #include "view/display-messages.h"
34 * @brief プレイヤー周辺の地形を感知する
35 * @param caster_ptr プレーヤーへの参照ポインタ
38 * @param known 地形から危険フラグを外すならTRUE
39 * @return 効力があった場合TRUEを返す
41 static bool detect_feat_flag(player_type *caster_ptr, POSITION range, int flag, bool known)
43 if (d_info[caster_ptr->dungeon_idx].flags.has(DF::DARKNESS))
48 for (POSITION y = 1; y < caster_ptr->current_floor_ptr->height - 1; y++) {
49 for (POSITION x = 1; x <= caster_ptr->current_floor_ptr->width - 1; x++) {
50 int dist = distance(caster_ptr->y, caster_ptr->x, y, x);
53 g_ptr = &caster_ptr->current_floor_ptr->grid_array[y][x];
54 if (flag == FF_TRAP) {
55 /* Mark as detected */
56 if (dist <= range && known) {
57 if (dist <= range - 1)
58 g_ptr->info |= (CAVE_IN_DETECT);
60 g_ptr->info &= ~(CAVE_UNSAFE);
62 lite_spot(caster_ptr, y, x);
66 if (cave_has_flag_grid(g_ptr, flag)) {
67 disclose_grid(caster_ptr, y, x);
68 g_ptr->info |= (CAVE_MARK);
69 lite_spot(caster_ptr, y, x);
79 * @brief プレイヤー周辺のトラップを感知する / Detect all traps on current panel
80 * @param caster_ptr プレーヤーへの参照ポインタ
82 * @param known 感知外範囲を超える警告フラグを立てる場合TRUEを返す
83 * @return 効力があった場合TRUEを返す
85 * 吟遊詩人による感知についてはFALSEを返す
87 bool detect_traps(player_type *caster_ptr, POSITION range, bool known)
89 bool detect = detect_feat_flag(caster_ptr, range, FF_TRAP, known);
91 detect_feat_flag(caster_ptr, range, FF_TRAP, true);
94 caster_ptr->dtrap = true;
96 if (music_singing(caster_ptr, MUSIC_DETECT) && get_singing_count(caster_ptr) > 0)
100 msg_print(_("トラップの存在を感じとった!", "You sense the presence of traps!"));
106 * @brief プレイヤー周辺のドアを感知する / Detect all doors on current panel
107 * @param caster_ptr プレーヤーへの参照ポインタ
109 * @return 効力があった場合TRUEを返す
111 bool detect_doors(player_type *caster_ptr, POSITION range)
113 bool detect = detect_feat_flag(caster_ptr, range, FF_DOOR, true);
115 if (music_singing(caster_ptr, MUSIC_DETECT) && get_singing_count(caster_ptr) > 0)
118 msg_print(_("ドアの存在を感じとった!", "You sense the presence of doors!"));
125 * @brief プレイヤー周辺の階段を感知する / Detect all stairs on current panel
126 * @param caster_ptr プレーヤーへの参照ポインタ
128 * @return 効力があった場合TRUEを返す
130 bool detect_stairs(player_type *caster_ptr, POSITION range)
132 bool detect = detect_feat_flag(caster_ptr, range, FF_STAIRS, true);
134 if (music_singing(caster_ptr, MUSIC_DETECT) && get_singing_count(caster_ptr) > 0)
137 msg_print(_("階段の存在を感じとった!", "You sense the presence of stairs!"));
144 * @brief プレイヤー周辺の地形財宝を感知する / Detect any treasure on the current panel
145 * @param caster_ptr プレーヤーへの参照ポインタ
147 * @return 効力があった場合TRUEを返す
149 bool detect_treasure(player_type *caster_ptr, POSITION range)
151 bool detect = detect_feat_flag(caster_ptr, range, FF_HAS_GOLD, true);
153 if (music_singing(caster_ptr, MUSIC_DETECT) && get_singing_count(caster_ptr) > 6)
156 msg_print(_("埋蔵された財宝の存在を感じとった!", "You sense the presence of buried treasure!"));
162 * @brief プレイヤー周辺のアイテム財宝を感知する / Detect all "gold" objects on the current panel
163 * @param caster_ptr プレーヤーへの参照ポインタ
165 * @return 効力があった場合TRUEを返す
167 bool detect_objects_gold(player_type *caster_ptr, POSITION range)
169 POSITION range2 = range;
170 if (d_info[caster_ptr->dungeon_idx].flags.has(DF::DARKNESS))
176 for (OBJECT_IDX i = 1; i < caster_ptr->current_floor_ptr->o_max; i++) {
177 object_type *o_ptr = &caster_ptr->current_floor_ptr->o_list[i];
179 if (!object_is_valid(o_ptr))
181 if (object_is_held_monster(o_ptr))
186 if (distance(caster_ptr->y, caster_ptr->x, y, x) > range2)
189 if (o_ptr->tval == TV_GOLD) {
190 o_ptr->marked |= OM_FOUND;
191 lite_spot(caster_ptr, y, x);
196 if (music_singing(caster_ptr, MUSIC_DETECT) && get_singing_count(caster_ptr) > 6)
199 msg_print(_("財宝の存在を感じとった!", "You sense the presence of treasure!"));
202 if (detect_monsters_string(caster_ptr, range, "$")) {
210 * @brief 通常のアイテムオブジェクトを感知する / Detect all "normal" objects on the current panel
211 * @param caster_ptr プレーヤーへの参照ポインタ
213 * @return 効力があった場合TRUEを返す
215 bool detect_objects_normal(player_type *caster_ptr, POSITION range)
217 POSITION range2 = range;
218 if (d_info[caster_ptr->dungeon_idx].flags.has(DF::DARKNESS))
222 for (OBJECT_IDX i = 1; i < caster_ptr->current_floor_ptr->o_max; i++) {
223 object_type *o_ptr = &caster_ptr->current_floor_ptr->o_list[i];
225 if (!object_is_valid(o_ptr))
227 if (object_is_held_monster(o_ptr))
230 POSITION y = o_ptr->iy;
231 POSITION x = o_ptr->ix;
233 if (distance(caster_ptr->y, caster_ptr->x, y, x) > range2)
236 if (o_ptr->tval != TV_GOLD) {
237 o_ptr->marked |= OM_FOUND;
238 lite_spot(caster_ptr, y, x);
243 if (music_singing(caster_ptr, MUSIC_DETECT) && get_singing_count(caster_ptr) > 6)
246 msg_print(_("アイテムの存在を感じとった!", "You sense the presence of objects!"));
249 if (detect_monsters_string(caster_ptr, range, "!=?|/`")) {
257 * @brief 魔法効果のあるのアイテムオブジェクトを感知する / Detect all "magic" objects on the current panel.
258 * @param caster_ptr プレーヤーへの参照ポインタ
260 * @return 効力があった場合TRUEを返す
263 * This will light up all spaces with "magic" items, including artifacts,
264 * ego-items, potions, scrolls, books, rods, wands, staffs, amulets, rings,
265 * and "enchanted" items of the "good" variety.
267 * It can probably be argued that this function is now too powerful.
270 bool detect_objects_magic(player_type *caster_ptr, POSITION range)
272 if (d_info[caster_ptr->dungeon_idx].flags.has(DF::DARKNESS))
277 for (OBJECT_IDX i = 1; i < caster_ptr->current_floor_ptr->o_max; i++) {
278 object_type *o_ptr = &caster_ptr->current_floor_ptr->o_list[i];
280 if (!object_is_valid(o_ptr))
282 if (object_is_held_monster(o_ptr))
285 POSITION y = o_ptr->iy;
286 POSITION x = o_ptr->ix;
288 if (distance(caster_ptr->y, caster_ptr->x, y, x) > range)
292 if (object_is_artifact(o_ptr) || object_is_ego(o_ptr) || (tv == TV_WHISTLE) || (tv == TV_AMULET) || (tv == TV_RING) || (tv == TV_STAFF)
293 || (tv == TV_WAND) || (tv == TV_ROD) || (tv == TV_SCROLL) || (tv == TV_POTION) || (tv == TV_LIFE_BOOK) || (tv == TV_SORCERY_BOOK)
294 || (tv == TV_NATURE_BOOK) || (tv == TV_CHAOS_BOOK) || (tv == TV_DEATH_BOOK) || (tv == TV_TRUMP_BOOK) || (tv == TV_ARCANE_BOOK)
295 || (tv == TV_CRAFT_BOOK) || (tv == TV_DEMON_BOOK) || (tv == TV_CRUSADE_BOOK) || (tv == TV_MUSIC_BOOK) || (tv == TV_HISSATSU_BOOK)
296 || (tv == TV_HEX_BOOK) || ((o_ptr->to_a > 0) || (o_ptr->to_h + o_ptr->to_d > 0))) {
297 o_ptr->marked |= OM_FOUND;
298 lite_spot(caster_ptr, y, x);
304 msg_print(_("魔法のアイテムの存在を感じとった!", "You sense the presence of magic objects!"));
311 * @brief 一般のモンスターを感知する / Detect all "normal" monsters on the current panel
312 * @param caster_ptr プレーヤーへの参照ポインタ
314 * @return 効力があった場合TRUEを返す
316 bool detect_monsters_normal(player_type *caster_ptr, POSITION range)
318 if (d_info[caster_ptr->dungeon_idx].flags.has(DF::DARKNESS))
322 for (MONSTER_IDX i = 1; i < caster_ptr->current_floor_ptr->m_max; i++) {
323 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[i];
324 monster_race *r_ptr = &r_info[m_ptr->r_idx];
325 if (!monster_is_valid(m_ptr))
328 POSITION y = m_ptr->fy;
329 POSITION x = m_ptr->fx;
330 if (distance(caster_ptr->y, caster_ptr->x, y, x) > range)
333 if (!(r_ptr->flags2 & RF2_INVISIBLE) || caster_ptr->see_inv) {
334 m_ptr->mflag2.set({MFLAG2::MARK, MFLAG2::SHOW});
335 update_monster(caster_ptr, i, false);
340 if (music_singing(caster_ptr, MUSIC_DETECT) && get_singing_count(caster_ptr) > 3)
343 msg_print(_("モンスターの存在を感じとった!", "You sense the presence of monsters!"));
350 * @brief 不可視のモンスターを感知する / Detect all "invisible" monsters around the player
351 * @param caster_ptr プレーヤーへの参照ポインタ
353 * @return 効力があった場合TRUEを返す
355 bool detect_monsters_invis(player_type *caster_ptr, POSITION range)
357 if (d_info[caster_ptr->dungeon_idx].flags.has(DF::DARKNESS))
361 for (MONSTER_IDX i = 1; i < caster_ptr->current_floor_ptr->m_max; i++) {
362 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[i];
363 monster_race *r_ptr = &r_info[m_ptr->r_idx];
365 if (!monster_is_valid(m_ptr))
368 POSITION y = m_ptr->fy;
369 POSITION x = m_ptr->fx;
371 if (distance(caster_ptr->y, caster_ptr->x, y, x) > range)
374 if (r_ptr->flags2 & RF2_INVISIBLE) {
375 if (caster_ptr->monster_race_idx == m_ptr->r_idx) {
376 caster_ptr->window_flags |= (PW_MONSTER);
379 m_ptr->mflag2.set({MFLAG2::MARK, MFLAG2::SHOW});
380 update_monster(caster_ptr, i, false);
385 if (music_singing(caster_ptr, MUSIC_DETECT) && get_singing_count(caster_ptr) > 3)
388 msg_print(_("透明な生物の存在を感じとった!", "You sense the presence of invisible creatures!"));
395 * @brief 邪悪なモンスターを感知する / Detect all "evil" monsters on current panel
396 * @param caster_ptr プレーヤーへの参照ポインタ
398 * @return 効力があった場合TRUEを返す
400 bool detect_monsters_evil(player_type *caster_ptr, POSITION range)
402 if (d_info[caster_ptr->dungeon_idx].flags.has(DF::DARKNESS))
406 for (MONSTER_IDX i = 1; i < caster_ptr->current_floor_ptr->m_max; i++) {
407 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[i];
408 monster_race *r_ptr = &r_info[m_ptr->r_idx];
409 if (!monster_is_valid(m_ptr))
412 POSITION y = m_ptr->fy;
413 POSITION x = m_ptr->fx;
415 if (distance(caster_ptr->y, caster_ptr->x, y, x) > range)
418 if (r_ptr->flags3 & RF3_EVIL) {
419 if (is_original_ap(m_ptr)) {
420 r_ptr->r_flags3 |= (RF3_EVIL);
421 if (caster_ptr->monster_race_idx == m_ptr->r_idx) {
422 caster_ptr->window_flags |= (PW_MONSTER);
426 m_ptr->mflag2.set({MFLAG2::MARK, MFLAG2::SHOW});
427 update_monster(caster_ptr, i, false);
433 msg_print(_("邪悪なる生物の存在を感じとった!", "You sense the presence of evil creatures!"));
440 * @brief 無生命のモンスターを感知する(アンデッド、悪魔系を含む) / Detect all "nonliving", "undead" or "demonic" monsters on current panel
441 * @param caster_ptr プレーヤーへの参照ポインタ
443 * @return 効力があった場合TRUEを返す
445 bool detect_monsters_nonliving(player_type *caster_ptr, POSITION range)
447 if (d_info[caster_ptr->dungeon_idx].flags.has(DF::DARKNESS))
451 for (MONSTER_IDX i = 1; i < caster_ptr->current_floor_ptr->m_max; i++) {
452 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[i];
453 if (!monster_is_valid(m_ptr))
456 POSITION y = m_ptr->fy;
457 POSITION x = m_ptr->fx;
458 if (distance(caster_ptr->y, caster_ptr->x, y, x) > range)
461 if (!monster_living(m_ptr->r_idx)) {
462 if (caster_ptr->monster_race_idx == m_ptr->r_idx) {
463 caster_ptr->window_flags |= (PW_MONSTER);
466 m_ptr->mflag2.set({MFLAG2::MARK, MFLAG2::SHOW});
467 update_monster(caster_ptr, i, false);
473 msg_print(_("自然でないモンスターの存在を感じた!", "You sense the presence of unnatural beings!"));
480 * @brief 精神のあるモンスターを感知する / Detect all monsters it has mind on current panel
481 * @param caster_ptr プレーヤーへの参照ポインタ
483 * @return 効力があった場合TRUEを返す
485 bool detect_monsters_mind(player_type *caster_ptr, POSITION range)
487 if (d_info[caster_ptr->dungeon_idx].flags.has(DF::DARKNESS))
491 for (MONSTER_IDX i = 1; i < caster_ptr->current_floor_ptr->m_max; i++) {
492 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[i];
493 monster_race *r_ptr = &r_info[m_ptr->r_idx];
494 if (!monster_is_valid(m_ptr))
497 POSITION y = m_ptr->fy;
498 POSITION x = m_ptr->fx;
500 if (distance(caster_ptr->y, caster_ptr->x, y, x) > range)
503 if (!(r_ptr->flags2 & RF2_EMPTY_MIND)) {
504 if (caster_ptr->monster_race_idx == m_ptr->r_idx) {
505 caster_ptr->window_flags |= (PW_MONSTER);
508 m_ptr->mflag2.set({MFLAG2::MARK, MFLAG2::SHOW});
509 update_monster(caster_ptr, i, false);
515 msg_print(_("殺気を感じとった!", "You sense the presence of someone's mind!"));
522 * @brief 該当シンボルのモンスターを感知する / Detect all (string) monsters on current panel
523 * @param caster_ptr プレーヤーへの参照ポインタ
525 * @param Match 対応シンボルの混じったモンスター文字列(複数指定化)
526 * @return 効力があった場合TRUEを返す
528 bool detect_monsters_string(player_type *caster_ptr, POSITION range, concptr Match)
530 if (d_info[caster_ptr->dungeon_idx].flags.has(DF::DARKNESS))
534 for (MONSTER_IDX i = 1; i < caster_ptr->current_floor_ptr->m_max; i++) {
535 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[i];
536 monster_race *r_ptr = &r_info[m_ptr->r_idx];
537 if (!monster_is_valid(m_ptr))
540 POSITION y = m_ptr->fy;
541 POSITION x = m_ptr->fx;
543 if (distance(caster_ptr->y, caster_ptr->x, y, x) > range)
546 if (angband_strchr(Match, r_ptr->d_char)) {
547 if (caster_ptr->monster_race_idx == m_ptr->r_idx) {
548 caster_ptr->window_flags |= (PW_MONSTER);
551 m_ptr->mflag2.set({MFLAG2::MARK, MFLAG2::SHOW});
552 update_monster(caster_ptr, i, false);
557 if (music_singing(caster_ptr, MUSIC_DETECT) && get_singing_count(caster_ptr) > 3)
560 msg_print(_("モンスターの存在を感じとった!", "You sense the presence of monsters!"));
567 * @brief flags3に対応するモンスターを感知する / A "generic" detect monsters routine, tagged to flags3
568 * @param caster_ptr プレーヤーへの参照ポインタ
570 * @param match_flag 感知フラグ
571 * @return 効力があった場合TRUEを返す
573 bool detect_monsters_xxx(player_type *caster_ptr, POSITION range, u32b match_flag)
575 if (d_info[caster_ptr->dungeon_idx].flags.has(DF::DARKNESS))
579 for (MONSTER_IDX i = 1; i < caster_ptr->current_floor_ptr->m_max; i++) {
580 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[i];
581 monster_race *r_ptr = &r_info[m_ptr->r_idx];
582 if (!monster_is_valid(m_ptr))
585 POSITION y = m_ptr->fy;
586 POSITION x = m_ptr->fx;
588 if (distance(caster_ptr->y, caster_ptr->x, y, x) > range)
591 if (r_ptr->flags3 & (match_flag)) {
592 if (is_original_ap(m_ptr)) {
593 r_ptr->r_flags3 |= (match_flag);
594 if (caster_ptr->monster_race_idx == m_ptr->r_idx) {
595 caster_ptr->window_flags |= (PW_MONSTER);
599 m_ptr->mflag2.set({MFLAG2::MARK, MFLAG2::SHOW});
600 update_monster(caster_ptr, i, false);
605 concptr desc_monsters = _("変なモンスター", "weird monsters");
607 switch (match_flag) {
609 desc_monsters = _("デーモン", "demons");
612 desc_monsters = _("アンデッド", "the undead");
616 msg_format(_("%sの存在を感じとった!", "You sense the presence of %s!"), desc_monsters);
624 * @brief 全感知処理 / Detect everything
625 * @param caster_ptr プレーヤーへの参照ポインタ
627 * @return 効力があった場合TRUEを返す
629 bool detect_all(player_type *caster_ptr, POSITION range)
632 if (detect_traps(caster_ptr, range, TRUE))
634 if (detect_doors(caster_ptr, range))
636 if (detect_stairs(caster_ptr, range))
638 if (detect_objects_gold(caster_ptr, range))
640 if (detect_objects_normal(caster_ptr, range))
642 if (detect_monsters_invis(caster_ptr, range))
644 if (detect_monsters_normal(caster_ptr, range))