OSDN Git Service

[Refactor] #2628 Renamed object-type-definition.cpp/h to item-entity.cpp/h
[hengbandforosx/hengbandosx.git] / src / spell-kind / spells-sight.cpp
1 #include "spell-kind/spells-sight.h"
2 #include "avatar/avatar.h"
3 #include "core/player-update-types.h"
4 #include "core/stuff-handler.h"
5 #include "core/window-redrawer.h"
6 #include "effect/attribute-types.h"
7 #include "effect/effect-characteristics.h"
8 #include "effect/effect-processor.h"
9 #include "floor/cave.h"
10 #include "game-option/birth-options.h"
11 #include "game-option/map-screen-options.h"
12 #include "grid/grid.h"
13 #include "io/cursor.h"
14 #include "io/input-key-acceptor.h"
15 #include "locale/english.h"
16 #include "lore/lore-store.h"
17 #include "monster-race/monster-kind-mask.h"
18 #include "monster-race/monster-race.h"
19 #include "monster-race/race-flags3.h"
20 #include "monster/monster-describer.h"
21 #include "monster/monster-description-types.h"
22 #include "monster/monster-flag-types.h"
23 #include "monster/monster-info.h"
24 #include "monster/monster-status-setter.h"
25 #include "monster/monster-status.h"
26 #include "monster/smart-learn-types.h"
27 #include "system/floor-type-definition.h"
28 #include "system/monster-race-definition.h"
29 #include "system/monster-type-definition.h"
30 #include "system/player-type-definition.h"
31 #include "target/projection-path-calculator.h"
32 #include "term/screen-processor.h"
33 #include "view/display-messages.h"
34
35 /*!
36  * @brief 視界内モンスターに魔法効果を与える / Apply a "project()" directly to all viewable monsters
37  * @param typ 属性効果
38  * @param dam 効果量
39  * @return 効力があった場合TRUEを返す
40  * @details
41  * <pre>
42  * Note that affected monsters are NOT auto-tracked by this usage.
43  *
44  * To avoid misbehavior when monster deaths have side-effects,
45  * this is done in two passes. -- JDL
46  * </pre>
47  */
48 bool project_all_los(PlayerType *player_ptr, AttributeType typ, int dam)
49 {
50     for (MONSTER_IDX i = 1; i < player_ptr->current_floor_ptr->m_max; i++) {
51         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
52         if (!m_ptr->is_valid()) {
53             continue;
54         }
55
56         POSITION y = m_ptr->fy;
57         POSITION x = m_ptr->fx;
58         if (!player_has_los_bold(player_ptr, y, x) || !projectable(player_ptr, player_ptr->y, player_ptr->x, y, x)) {
59             continue;
60         }
61
62         m_ptr->mflag.set(MonsterTemporaryFlagType::LOS);
63     }
64
65     BIT_FLAGS flg = PROJECT_JUMP | PROJECT_KILL | PROJECT_HIDE;
66     bool obvious = false;
67     for (MONSTER_IDX i = 1; i < player_ptr->current_floor_ptr->m_max; i++) {
68         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
69         if (m_ptr->mflag.has_not(MonsterTemporaryFlagType::LOS)) {
70             continue;
71         }
72
73         m_ptr->mflag.reset(MonsterTemporaryFlagType::LOS);
74         POSITION y = m_ptr->fy;
75         POSITION x = m_ptr->fx;
76
77         if (project(player_ptr, 0, 0, y, x, dam, typ, flg).notice) {
78             obvious = true;
79         }
80     }
81
82     return obvious;
83 }
84
85 /*!
86  * @brief 視界内モンスターを加速する処理 / Speed monsters
87  * @param player_ptr プレイヤーへの参照ポインタ
88  * @return 効力があった場合TRUEを返す
89  */
90 bool speed_monsters(PlayerType *player_ptr)
91 {
92     return project_all_los(player_ptr, AttributeType::OLD_SPEED, player_ptr->lev);
93 }
94
95 /*!
96  * @brief 視界内モンスターを加速する処理 / Slow monsters
97  * @param player_ptr プレイヤーへの参照ポインタ
98  * @return 効力があった場合TRUEを返す
99  */
100 bool slow_monsters(PlayerType *player_ptr, int power)
101 {
102     return project_all_los(player_ptr, AttributeType::OLD_SLOW, power);
103 }
104
105 /*!
106  * @brief 視界内モンスターを眠らせる処理 / Sleep monsters
107  * @param player_ptr プレイヤーへの参照ポインタ
108  * @return 効力があった場合TRUEを返す
109  */
110 bool sleep_monsters(PlayerType *player_ptr, int power)
111 {
112     return project_all_los(player_ptr, AttributeType::OLD_SLEEP, power);
113 }
114
115 /*!
116  * @brief 視界内の邪悪なモンスターをテレポート・アウェイさせる処理 / Banish evil monsters
117  * @param player_ptr プレイヤーへの参照ポインタ
118  * @return 効力があった場合TRUEを返す
119  */
120 bool banish_evil(PlayerType *player_ptr, int dist)
121 {
122     return project_all_los(player_ptr, AttributeType::AWAY_EVIL, dist);
123 }
124
125 /*!
126  * @brief 視界内のアンデッド・モンスターを恐怖させる処理 / Turn undead
127  * @return 効力があった場合TRUEを返す
128  */
129 bool turn_undead(PlayerType *player_ptr)
130 {
131     bool tester = (project_all_los(player_ptr, AttributeType::TURN_UNDEAD, player_ptr->lev));
132     if (tester) {
133         chg_virtue(player_ptr, V_UNLIFE, -1);
134     }
135     return tester;
136 }
137
138 /*!
139  * @brief 視界内のアンデッド・モンスターにダメージを与える処理 / Dispel undead monsters
140  * @param player_ptr プレイヤーへの参照ポインタ
141  * @return 効力があった場合TRUEを返す
142  */
143 bool dispel_undead(PlayerType *player_ptr, int dam)
144 {
145     bool tester = (project_all_los(player_ptr, AttributeType::DISP_UNDEAD, dam));
146     if (tester) {
147         chg_virtue(player_ptr, V_UNLIFE, -2);
148     }
149     return tester;
150 }
151
152 /*!
153  * @brief 視界内の邪悪なモンスターにダメージを与える処理 / Dispel evil monsters
154  * @param player_ptr プレイヤーへの参照ポインタ
155  * @return 効力があった場合TRUEを返す
156  */
157 bool dispel_evil(PlayerType *player_ptr, int dam)
158 {
159     return project_all_los(player_ptr, AttributeType::DISP_EVIL, dam);
160 }
161
162 /*!
163  * @brief 視界内の善良なモンスターにダメージを与える処理 / Dispel good monsters
164  * @param player_ptr プレイヤーへの参照ポインタ
165  * @return 効力があった場合TRUEを返す
166  */
167 bool dispel_good(PlayerType *player_ptr, int dam)
168 {
169     return project_all_los(player_ptr, AttributeType::DISP_GOOD, dam);
170 }
171
172 /*!
173  * @brief 視界内のあらゆるモンスターにダメージを与える処理 / Dispel all monsters
174  * @param player_ptr プレイヤーへの参照ポインタ
175  * @return 効力があった場合TRUEを返す
176  */
177 bool dispel_monsters(PlayerType *player_ptr, int dam)
178 {
179     return project_all_los(player_ptr, AttributeType::DISP_ALL, dam);
180 }
181
182 /*!
183  * @brief 視界内の生命のあるモンスターにダメージを与える処理 / Dispel 'living' monsters
184  * @param player_ptr プレイヤーへの参照ポインタ
185  * @return 効力があった場合TRUEを返す
186  */
187 bool dispel_living(PlayerType *player_ptr, int dam)
188 {
189     return project_all_los(player_ptr, AttributeType::DISP_LIVING, dam);
190 }
191
192 /*!
193  * @brief 視界内の悪魔系モンスターにダメージを与える処理 / Dispel 'living' monsters
194  * @param player_ptr プレイヤーへの参照ポインタ
195  * @return 効力があった場合TRUEを返す
196  */
197 bool dispel_demons(PlayerType *player_ptr, int dam)
198 {
199     return project_all_los(player_ptr, AttributeType::DISP_DEMON, dam);
200 }
201
202 /*!
203  * @brief 視界内のモンスターに「聖戦」効果を与える処理
204  * @param player_ptr プレイヤーへの参照ポインタ
205  * @return 効力があった場合TRUEを返す
206  */
207 bool crusade(PlayerType *player_ptr)
208 {
209     return project_all_los(player_ptr, AttributeType::CRUSADE, player_ptr->lev * 4);
210 }
211
212 /*!
213  * @brief 視界内モンスターを怒らせる処理 / Wake up all monsters, and speed up "los" monsters.
214  * @param player_ptr プレイヤーへの参照ポインタ
215  * @param who 怒らせる原因を起こしたモンスター(0ならばプレイヤー)
216  */
217 void aggravate_monsters(PlayerType *player_ptr, MONSTER_IDX who)
218 {
219     bool sleep = false;
220     bool speed = false;
221     for (MONSTER_IDX i = 1; i < player_ptr->current_floor_ptr->m_max; i++) {
222         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
223         if (!m_ptr->is_valid()) {
224             continue;
225         }
226         if (i == who) {
227             continue;
228         }
229
230         if (m_ptr->cdis < MAX_PLAYER_SIGHT * 2) {
231             if (m_ptr->is_asleep()) {
232                 (void)set_monster_csleep(player_ptr, i, 0);
233                 sleep = true;
234             }
235
236             if (!m_ptr->is_pet()) {
237                 m_ptr->mflag2.set(MonsterConstantFlagType::NOPET);
238             }
239         }
240
241         if (player_has_los_bold(player_ptr, m_ptr->fy, m_ptr->fx)) {
242             if (!m_ptr->is_pet()) {
243                 (void)set_monster_fast(player_ptr, i, m_ptr->get_remaining_acceleration() + 100);
244                 speed = true;
245             }
246         }
247     }
248
249     if (speed) {
250         msg_print(_("付近で何かが突如興奮したような感じを受けた!", "You feel a sudden stirring nearby!"));
251     } else if (sleep) {
252         msg_print(_("何かが突如興奮したような騒々しい音が遠くに聞こえた!", "You hear a sudden stirring in the distance!"));
253     }
254     if (player_ptr->riding) {
255         player_ptr->update |= PU_BONUS;
256     }
257 }
258
259 /*!
260  * @brief パニック・モンスター効果(プレイヤー視界範囲内) / Confuse monsters
261  * @param player_ptr プレイヤーへの参照ポインタ
262  * @param dam 効力
263  * @return 作用が実際にあった場合TRUEを返す
264  */
265 bool confuse_monsters(PlayerType *player_ptr, int dam)
266 {
267     return project_all_los(player_ptr, AttributeType::OLD_CONF, dam);
268 }
269
270 /*!
271  * @brief チャーム・モンスター効果(プレイヤー視界範囲内) / Charm monsters
272  * @param player_ptr プレイヤーへの参照ポインタ
273  * @param dam 効力
274  * @return 作用が実際にあった場合TRUEを返す
275  */
276 bool charm_monsters(PlayerType *player_ptr, int dam)
277 {
278     return project_all_los(player_ptr, AttributeType::CHARM, dam);
279 }
280
281 /*!
282  * @brief 動物魅了効果(プレイヤー視界範囲内) / Charm Animals
283  * @param player_ptr プレイヤーへの参照ポインタ
284  * @param dam 効力
285  * @return 作用が実際にあった場合TRUEを返す
286  */
287 bool charm_animals(PlayerType *player_ptr, int dam)
288 {
289     return project_all_los(player_ptr, AttributeType::CONTROL_ANIMAL, dam);
290 }
291
292 /*!
293  * @brief モンスター朦朧効果(プレイヤー視界範囲内) / Stun monsters
294  * @param player_ptr プレイヤーへの参照ポインタ
295  * @param dam 効力
296  * @return 作用が実際にあった場合TRUEを返す
297  */
298 bool stun_monsters(PlayerType *player_ptr, int dam)
299 {
300     return project_all_los(player_ptr, AttributeType::STUN, dam);
301 }
302
303 /*!
304  * @brief モンスター停止効果(プレイヤー視界範囲内) / Stasis monsters
305  * @param player_ptr プレイヤーへの参照ポインタ
306  * @param dam 効力
307  * @return 作用が実際にあった場合TRUEを返す
308  */
309 bool stasis_monsters(PlayerType *player_ptr, int dam)
310 {
311     return project_all_los(player_ptr, AttributeType::STASIS, dam);
312 }
313
314 /*!
315  * @brief モンスター精神攻撃効果(プレイヤー視界範囲内) / Mindblast monsters
316  * @param player_ptr プレイヤーへの参照ポインタ
317  * @param dam 効力
318  * @return 作用が実際にあった場合TRUEを返す
319  */
320 bool mindblast_monsters(PlayerType *player_ptr, int dam)
321 {
322     return project_all_los(player_ptr, AttributeType::PSI, dam);
323 }
324
325 /*!
326  * @brief モンスター追放効果(プレイヤー視界範囲内) / Banish all monsters
327  * @param player_ptr プレイヤーへの参照ポインタ
328  * @param dist 効力(距離)
329  * @return 作用が実際にあった場合TRUEを返す
330  */
331 bool banish_monsters(PlayerType *player_ptr, int dist)
332 {
333     return project_all_los(player_ptr, AttributeType::AWAY_ALL, dist);
334 }
335
336 /*!
337  * @brief 邪悪退散効果(プレイヤー視界範囲内) / Turn evil
338  * @param player_ptr プレイヤーへの参照ポインタ
339  * @param dam 効力
340  * @return 作用が実際にあった場合TRUEを返す
341  */
342 bool turn_evil(PlayerType *player_ptr, int dam)
343 {
344     return project_all_los(player_ptr, AttributeType::TURN_EVIL, dam);
345 }
346
347 /*!
348  * @brief 全モンスター退散効果(プレイヤー視界範囲内) / Turn everyone
349  * @param player_ptr プレイヤーへの参照ポインタ
350  * @param dam 効力
351  * @return 作用が実際にあった場合TRUEを返す
352  */
353 bool turn_monsters(PlayerType *player_ptr, int dam)
354 {
355     return project_all_los(player_ptr, AttributeType::TURN_ALL, dam);
356 }
357
358 /*!
359  * @brief 死の光線(プレイヤー視界範囲内) / Death-ray all monsters (note: OBSCENELY powerful)
360  * @param player_ptr プレイヤーへの参照ポインタ
361  * @return 作用が実際にあった場合TRUEを返す
362  */
363 bool deathray_monsters(PlayerType *player_ptr)
364 {
365     return project_all_los(player_ptr, AttributeType::DEATH_RAY, player_ptr->lev * 200);
366 }
367
368 /*!
369  * @brief 調査したモンスターの情報を表示する
370  * @param player_ptr プレイヤー情報への参照ポインタ
371  * @param m_ptr モンスター情報への参照ポインタ
372  * @param r_ptr モンスター種族への参照ポインタ
373  */
374 void probed_monster_info(char *buf, PlayerType *player_ptr, MonsterEntity *m_ptr, MonsterRaceInfo *r_ptr)
375 {
376     if (!m_ptr->is_original_ap()) {
377         if (m_ptr->mflag2.has(MonsterConstantFlagType::KAGE)) {
378             m_ptr->mflag2.reset(MonsterConstantFlagType::KAGE);
379         }
380
381         m_ptr->ap_r_idx = m_ptr->r_idx;
382         lite_spot(player_ptr, m_ptr->fy, m_ptr->fx);
383     }
384
385     GAME_TEXT m_name[MAX_NLEN];
386     monster_desc(player_ptr, m_name, m_ptr, MD_IGNORE_HALLU | MD_INDEF_HIDDEN);
387
388     concptr align;
389     if (r_ptr->kind_flags.has_all_of(alignment_mask)) {
390         align = _("善悪", "good&evil");
391     } else if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
392         align = _("邪悪", "evil");
393     } else if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
394         align = _("善良", "good");
395     } else if ((m_ptr->sub_align & (SUB_ALIGN_EVIL | SUB_ALIGN_GOOD)) == (SUB_ALIGN_EVIL | SUB_ALIGN_GOOD)) {
396         align = _("中立(善悪)", "neutral(good&evil)");
397     } else if (m_ptr->sub_align & SUB_ALIGN_EVIL) {
398         align = _("中立(邪悪)", "neutral(evil)");
399     } else if (m_ptr->sub_align & SUB_ALIGN_GOOD) {
400         align = _("中立(善良)", "neutral(good)");
401     } else {
402         align = _("中立", "neutral");
403     }
404
405     const auto speed = m_ptr->get_temporary_speed() - STANDARD_SPEED;
406     sprintf(buf, _("%s ... 属性:%s HP:%d/%d AC:%d 速度:%s%d 経験:", "%s ... align:%s HP:%d/%d AC:%d speed:%s%d exp:"), m_name, align, (int)m_ptr->hp,
407         (int)m_ptr->maxhp, r_ptr->ac, (speed > 0) ? "+" : "", speed);
408
409     if (MonsterRace(r_ptr->next_r_idx).is_valid()) {
410         strcat(buf, format("%d/%d ", m_ptr->exp, r_ptr->next_exp));
411     } else {
412         strcat(buf, "xxx ");
413     }
414
415     if (m_ptr->is_asleep()) {
416         strcat(buf, _("睡眠 ", "sleeping "));
417     }
418     if (m_ptr->is_stunned()) {
419         strcat(buf, _("朦朧 ", "stunned "));
420     }
421     if (m_ptr->is_fearful()) {
422         strcat(buf, _("恐怖 ", "scared "));
423     }
424     if (m_ptr->is_confused()) {
425         strcat(buf, _("混乱 ", "confused "));
426     }
427     if (m_ptr->is_invulnerable()) {
428         strcat(buf, _("無敵 ", "invulnerable "));
429     }
430     buf[strlen(buf) - 1] = '\0';
431 }
432
433 /*!
434  * @brief 周辺モンスターを調査する / Probe nearby monsters
435  * @return 効力があった場合TRUEを返す
436  */
437 bool probing(PlayerType *player_ptr)
438 {
439     bool cu = game_term->scr->cu;
440     bool cv = game_term->scr->cv;
441     game_term->scr->cu = 0;
442     game_term->scr->cv = 1;
443
444     bool probe = false;
445     char buf[256];
446     for (int i = 1; i < player_ptr->current_floor_ptr->m_max; i++) {
447         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
448         auto *r_ptr = &monraces_info[m_ptr->r_idx];
449         if (!m_ptr->is_valid()) {
450             continue;
451         }
452         if (!player_has_los_bold(player_ptr, m_ptr->fy, m_ptr->fx)) {
453             continue;
454         }
455         if (!m_ptr->ml) {
456             continue;
457         }
458
459         if (!probe) {
460             msg_print(_("調査中...", "Probing..."));
461         }
462         msg_print(nullptr);
463
464         probed_monster_info(buf, player_ptr, m_ptr, r_ptr);
465         prt(buf, 0, 0);
466
467         message_add(buf);
468         player_ptr->window_flags |= (PW_MESSAGE);
469         handle_stuff(player_ptr);
470         move_cursor_relative(m_ptr->fy, m_ptr->fx);
471         inkey();
472         term_erase(0, 0, 255);
473         if (lore_do_probe(player_ptr, m_ptr->r_idx)) {
474             strcpy(buf, (r_ptr->name.data()));
475 #ifdef JP
476             msg_format("%sについてさらに詳しくなった気がする。", buf);
477 #else
478             plural_aux(buf);
479             msg_format("You now know more about %s.", buf);
480 #endif
481             msg_print(nullptr);
482         }
483
484         probe = true;
485     }
486
487     game_term->scr->cu = cu;
488     game_term->scr->cv = cv;
489     term_fresh();
490
491     if (probe) {
492         chg_virtue(player_ptr, V_KNOWLEDGE, 1);
493         msg_print(_("これで全部です。", "That's all."));
494     }
495
496     return probe;
497 }