OSDN Git Service

[Refactor] race-flags#.h のインクルードを削除
[hengbandforosx/hengbandosx.git] / src / spell-kind / spells-detection.cpp
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"
7 #include "grid/grid.h"
8 #include "grid/trap.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"
30
31 /*!
32  * @brief プレイヤー周辺の地形を感知する
33  * @param player_ptr プレイヤーへの参照ポインタ
34  * @param range 効果範囲
35  * @param flag 特定地形ID
36  * @param known 地形から危険フラグを外すならTRUE
37  * @return 効力があった場合TRUEを返す
38  */
39 static bool detect_feat_flag(PlayerType *player_ptr, POSITION range, TerrainCharacteristics flag, bool known)
40 {
41     auto &floor = *player_ptr->current_floor_ptr;
42     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
43         range /= 3;
44     }
45
46     bool detect = false;
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);
50             if (dist > range) {
51                 continue;
52             }
53
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);
60                     }
61
62                     g_ptr->info &= ~(CAVE_UNSAFE);
63
64                     lite_spot(player_ptr, y, x);
65                 }
66             }
67
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);
72                 detect = true;
73             }
74         }
75     }
76
77     return detect;
78 }
79
80 /*!
81  * @brief プレイヤー周辺のトラップを感知する / Detect all traps on current panel
82  * @param player_ptr プレイヤーへの参照ポインタ
83  * @param range 効果範囲
84  * @param known 感知外範囲を超える警告フラグを立てる場合TRUEを返す
85  * @return 効力があった場合TRUEを返す
86  * @details
87  * 吟遊詩人による感知についてはFALSEを返す
88  */
89 bool detect_traps(PlayerType *player_ptr, POSITION range, bool known)
90 {
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);
94     }
95
96     if (known || detect) {
97         player_ptr->dtrap = true;
98     }
99
100     if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 0) {
101         detect = false;
102     }
103
104     if (detect) {
105         msg_print(_("トラップの存在を感じとった!", "You sense the presence of traps!"));
106     }
107
108     return detect;
109 }
110
111 /*!
112  * @brief プレイヤー周辺のドアを感知する / Detect all doors on current panel
113  * @param player_ptr プレイヤーへの参照ポインタ
114  * @param range 効果範囲
115  * @return 効力があった場合TRUEを返す
116  */
117 bool detect_doors(PlayerType *player_ptr, POSITION range)
118 {
119     bool detect = detect_feat_flag(player_ptr, range, TerrainCharacteristics::DOOR, true);
120
121     if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 0) {
122         detect = false;
123     }
124     if (detect) {
125         msg_print(_("ドアの存在を感じとった!", "You sense the presence of doors!"));
126     }
127
128     return detect;
129 }
130
131 /*!
132  * @brief プレイヤー周辺の階段を感知する / Detect all stairs on current panel
133  * @param player_ptr プレイヤーへの参照ポインタ
134  * @param range 効果範囲
135  * @return 効力があった場合TRUEを返す
136  */
137 bool detect_stairs(PlayerType *player_ptr, POSITION range)
138 {
139     bool detect = detect_feat_flag(player_ptr, range, TerrainCharacteristics::STAIRS, true);
140
141     if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 0) {
142         detect = false;
143     }
144     if (detect) {
145         msg_print(_("階段の存在を感じとった!", "You sense the presence of stairs!"));
146     }
147
148     return detect;
149 }
150
151 /*!
152  * @brief プレイヤー周辺の地形財宝を感知する / Detect any treasure on the current panel
153  * @param player_ptr プレイヤーへの参照ポインタ
154  * @param range 効果範囲
155  * @return 効力があった場合TRUEを返す
156  */
157 bool detect_treasure(PlayerType *player_ptr, POSITION range)
158 {
159     bool detect = detect_feat_flag(player_ptr, range, TerrainCharacteristics::HAS_GOLD, true);
160
161     if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 6) {
162         detect = false;
163     }
164     if (detect) {
165         msg_print(_("埋蔵された財宝の存在を感じとった!", "You sense the presence of buried treasure!"));
166     }
167
168     return detect;
169 }
170 /*!
171  * @brief プレイヤー周辺のアイテム財宝を感知する / Detect all "gold" objects on the current panel
172  * @param player_ptr プレイヤーへの参照ポインタ
173  * @param range 効果範囲
174  * @return 効力があった場合TRUEを返す
175  */
176 bool detect_objects_gold(PlayerType *player_ptr, POSITION range)
177 {
178     auto &floor = *player_ptr->current_floor_ptr;
179     POSITION range2 = range;
180     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
181         range2 /= 3;
182     }
183
184     /* Scan objects */
185     bool detect = false;
186     POSITION y, x;
187     for (OBJECT_IDX i = 1; i < floor.o_max; i++) {
188         auto *o_ptr = &floor.o_list[i];
189
190         if (!o_ptr->is_valid()) {
191             continue;
192         }
193         if (o_ptr->is_held_by_monster()) {
194             continue;
195         }
196
197         y = o_ptr->iy;
198         x = o_ptr->ix;
199         if (distance(player_ptr->y, player_ptr->x, y, x) > range2) {
200             continue;
201         }
202
203         if (o_ptr->bi_key.tval() == ItemKindType::GOLD) {
204             o_ptr->marked.set(OmType::FOUND);
205             lite_spot(player_ptr, y, x);
206             detect = true;
207         }
208     }
209
210     if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 6) {
211         detect = false;
212     }
213     if (detect) {
214         msg_print(_("財宝の存在を感じとった!", "You sense the presence of treasure!"));
215     }
216
217     if (detect_monsters_string(player_ptr, range, "$")) {
218         detect = true;
219     }
220
221     return detect;
222 }
223
224 /*!
225  * @brief 通常のアイテムオブジェクトを感知する / Detect all "normal" objects on the current panel
226  * @param player_ptr プレイヤーへの参照ポインタ
227  * @param range 効果範囲
228  * @return 効力があった場合TRUEを返す
229  */
230 bool detect_objects_normal(PlayerType *player_ptr, POSITION range)
231 {
232     auto &floor = *player_ptr->current_floor_ptr;
233     POSITION range2 = range;
234     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
235         range2 /= 3;
236     }
237
238     bool detect = false;
239     for (OBJECT_IDX i = 1; i < floor.o_max; i++) {
240         auto *o_ptr = &floor.o_list[i];
241
242         if (!o_ptr->is_valid()) {
243             continue;
244         }
245         if (o_ptr->is_held_by_monster()) {
246             continue;
247         }
248
249         POSITION y = o_ptr->iy;
250         POSITION x = o_ptr->ix;
251
252         if (distance(player_ptr->y, player_ptr->x, y, x) > range2) {
253             continue;
254         }
255
256         if (o_ptr->bi_key.tval() != ItemKindType::GOLD) {
257             o_ptr->marked.set(OmType::FOUND);
258             lite_spot(player_ptr, y, x);
259             detect = true;
260         }
261     }
262
263     if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 6) {
264         detect = false;
265     }
266     if (detect) {
267         RedrawingFlagsUpdater::get_instance().set_flag(SubWindowRedrawingFlag::FOUND_ITEMS);
268         msg_print(_("アイテムの存在を感じとった!", "You sense the presence of objects!"));
269     }
270
271     if (detect_monsters_string(player_ptr, range, "!=?|/`")) {
272         detect = true;
273     }
274
275     return detect;
276 }
277
278 static bool is_object_magically(const ItemKindType tval)
279 {
280     switch (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:
289         return true;
290     default:
291         return false;
292     }
293 }
294
295 /*!
296  * @brief 魔法効果のあるのアイテムオブジェクトを感知する
297  * @param player_ptr プレイヤーへの参照ポインタ
298  * @param range 効果範囲
299  * @return 1つ以上感知したか否か
300  */
301 bool detect_objects_magic(PlayerType *player_ptr, POSITION range)
302 {
303     auto &floor = *player_ptr->current_floor_ptr;
304     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
305         range /= 3;
306     }
307
308     auto detect = false;
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()) {
312             continue;
313         }
314
315         auto y = o_ptr->iy;
316         auto x = o_ptr->ix;
317         if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
318             continue;
319         }
320
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);
326             detect = true;
327         }
328     }
329
330     if (detect) {
331         RedrawingFlagsUpdater::get_instance().set_flag(SubWindowRedrawingFlag::FOUND_ITEMS);
332         msg_print(_("魔法のアイテムの存在を感じとった!", "You sense the presence of magic objects!"));
333     }
334
335     return detect;
336 }
337
338 /*!
339  * @brief 一般のモンスターを感知する / Detect all "normal" monsters on the current panel
340  * @param player_ptr プレイヤーへの参照ポインタ
341  * @param range 効果範囲
342  * @return 効力があった場合TRUEを返す
343  */
344 bool detect_monsters_normal(PlayerType *player_ptr, POSITION range)
345 {
346     auto &floor = *player_ptr->current_floor_ptr;
347     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
348         range /= 3;
349     }
350
351     bool flag = false;
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()) {
356             continue;
357         }
358
359         POSITION y = m_ptr->fy;
360         POSITION x = m_ptr->fx;
361         if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
362             continue;
363         }
364
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);
368             flag = true;
369         }
370     }
371
372     if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 3) {
373         flag = false;
374     }
375     if (flag) {
376         msg_print(_("モンスターの存在を感じとった!", "You sense the presence of monsters!"));
377     }
378
379     return flag;
380 }
381
382 /*!
383  * @brief 不可視のモンスターを感知する / Detect all "invisible" monsters around the player
384  * @param player_ptr プレイヤーへの参照ポインタ
385  * @param range 効果範囲
386  * @return 効力があった場合TRUEを返す
387  */
388 bool detect_monsters_invis(PlayerType *player_ptr, POSITION range)
389 {
390     auto &floor = *player_ptr->current_floor_ptr;
391     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
392         range /= 3;
393     }
394
395     auto &rfu = RedrawingFlagsUpdater::get_instance();
396     auto flag = false;
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();
400
401         if (!m_ptr->is_valid()) {
402             continue;
403         }
404
405         POSITION y = m_ptr->fy;
406         POSITION x = m_ptr->fx;
407
408         if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
409             continue;
410         }
411
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);
415             }
416
417             m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
418             update_monster(player_ptr, i, false);
419             flag = true;
420         }
421     }
422
423     if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 3) {
424         flag = false;
425     }
426     if (flag) {
427         msg_print(_("透明な生物の存在を感じとった!", "You sense the presence of invisible creatures!"));
428     }
429
430     return flag;
431 }
432
433 /*!
434  * @brief 邪悪なモンスターを感知する / Detect all "evil" monsters on current panel
435  * @param player_ptr プレイヤーへの参照ポインタ
436  * @param range 効果範囲
437  * @return 効力があった場合TRUEを返す
438  */
439 bool detect_monsters_evil(PlayerType *player_ptr, POSITION range)
440 {
441     auto &floor = *player_ptr->current_floor_ptr;
442     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
443         range /= 3;
444     }
445
446     auto &rfu = RedrawingFlagsUpdater::get_instance();
447     auto flag = false;
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()) {
452             continue;
453         }
454
455         POSITION y = m_ptr->fy;
456         POSITION x = m_ptr->fx;
457
458         if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
459             continue;
460         }
461
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);
467                 }
468             }
469
470             m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
471             update_monster(player_ptr, i, false);
472             flag = true;
473         }
474     }
475
476     if (flag) {
477         msg_print(_("邪悪なる生物の存在を感じとった!", "You sense the presence of evil creatures!"));
478     }
479
480     return flag;
481 }
482
483 /*!
484  * @brief 無生命のモンスターを感知する(アンデッド、悪魔系を含む) / Detect all "nonliving", "undead" or "demonic" monsters on current panel
485  * @param player_ptr プレイヤーへの参照ポインタ
486  * @param range 効果範囲
487  * @return 効力があった場合TRUEを返す
488  */
489 bool detect_monsters_nonliving(PlayerType *player_ptr, POSITION range)
490 {
491     auto &floor = *player_ptr->current_floor_ptr;
492     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
493         range /= 3;
494     }
495
496     auto &rfu = RedrawingFlagsUpdater::get_instance();
497     auto flag = false;
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()) {
501             continue;
502         }
503
504         POSITION y = m_ptr->fy;
505         POSITION x = m_ptr->fx;
506         if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
507             continue;
508         }
509
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);
513             }
514
515             m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
516             update_monster(player_ptr, i, false);
517             flag = true;
518         }
519     }
520
521     if (flag) {
522         msg_print(_("自然でないモンスターの存在を感じた!", "You sense the presence of unnatural beings!"));
523     }
524
525     return flag;
526 }
527
528 /*!
529  * @brief 精神のあるモンスターを感知する / Detect all monsters it has mind on current panel
530  * @param player_ptr プレイヤーへの参照ポインタ
531  * @param range 効果範囲
532  * @return 効力があった場合TRUEを返す
533  */
534 bool detect_monsters_mind(PlayerType *player_ptr, POSITION range)
535 {
536     auto &floor = *player_ptr->current_floor_ptr;
537     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
538         range /= 3;
539     }
540
541     auto &rfu = RedrawingFlagsUpdater::get_instance();
542     auto flag = false;
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()) {
547             continue;
548         }
549
550         POSITION y = m_ptr->fy;
551         POSITION x = m_ptr->fx;
552
553         if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
554             continue;
555         }
556
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);
560             }
561
562             m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
563             update_monster(player_ptr, i, false);
564             flag = true;
565         }
566     }
567
568     if (flag) {
569         msg_print(_("殺気を感じとった!", "You sense the presence of someone's mind!"));
570     }
571
572     return flag;
573 }
574
575 /*!
576  * @brief 該当シンボルのモンスターを感知する / Detect all (string) monsters on current panel
577  * @param player_ptr プレイヤーへの参照ポインタ
578  * @param range 効果範囲
579  * @param Match 対応シンボルの混じったモンスター文字列(複数指定化)
580  * @return 効力があった場合TRUEを返す
581  */
582 bool detect_monsters_string(PlayerType *player_ptr, POSITION range, concptr Match)
583 {
584     auto &floor = *player_ptr->current_floor_ptr;
585     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
586         range /= 3;
587     }
588
589     auto &rfu = RedrawingFlagsUpdater::get_instance();
590     auto flag = false;
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()) {
595             continue;
596         }
597
598         POSITION y = m_ptr->fy;
599         POSITION x = m_ptr->fx;
600
601         if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
602             continue;
603         }
604
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);
608             }
609
610             m_ptr->mflag2.set({ MonsterConstantFlagType::MARK, MonsterConstantFlagType::SHOW });
611             update_monster(player_ptr, i, false);
612             flag = true;
613         }
614     }
615
616     if (music_singing(player_ptr, MUSIC_DETECT) && get_singing_count(player_ptr) > 3) {
617         flag = false;
618     }
619     if (flag) {
620         msg_print(_("モンスターの存在を感じとった!", "You sense the presence of monsters!"));
621     }
622
623     return flag;
624 }
625
626 /*!
627  * @brief 全感知処理 / Detect everything
628  * @param player_ptr プレイヤーへの参照ポインタ
629  * @param range 効果範囲
630  * @return 効力があった場合TRUEを返す
631  */
632 bool detect_all(PlayerType *player_ptr, POSITION range)
633 {
634     bool detect = false;
635     if (detect_traps(player_ptr, range, true)) {
636         detect = true;
637     }
638     if (detect_doors(player_ptr, range)) {
639         detect = true;
640     }
641     if (detect_stairs(player_ptr, range)) {
642         detect = true;
643     }
644     if (detect_objects_gold(player_ptr, range)) {
645         detect = true;
646     }
647     if (detect_objects_normal(player_ptr, range)) {
648         detect = true;
649     }
650     if (detect_monsters_invis(player_ptr, range)) {
651         detect = true;
652     }
653     if (detect_monsters_normal(player_ptr, range)) {
654         detect = true;
655     }
656     return detect;
657 }