OSDN Git Service

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