OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / spell-kind / spells-sight.cpp
1 #include "spell-kind/spells-sight.h"
2 #include "avatar/avatar.h"
3 #include "core/stuff-handler.h"
4 #include "core/window-redrawer.h"
5 #include "effect/attribute-types.h"
6 #include "effect/effect-characteristics.h"
7 #include "effect/effect-processor.h"
8 #include "floor/cave.h"
9 #include "game-option/birth-options.h"
10 #include "game-option/map-screen-options.h"
11 #include "grid/grid.h"
12 #include "io/cursor.h"
13 #include "io/input-key-acceptor.h"
14 #include "locale/english.h"
15 #include "lore/lore-store.h"
16 #include "monster-race/monster-kind-mask.h"
17 #include "monster-race/monster-race.h"
18 #include "monster-race/race-flags3.h"
19 #include "monster/monster-describer.h"
20 #include "monster/monster-description-types.h"
21 #include "monster/monster-flag-types.h"
22 #include "monster/monster-info.h"
23 #include "monster/monster-status-setter.h"
24 #include "monster/monster-status.h"
25 #include "monster/smart-learn-types.h"
26 #include "system/floor-type-definition.h"
27 #include "system/monster-entity.h"
28 #include "system/monster-race-info.h"
29 #include "system/player-type-definition.h"
30 #include "system/redrawing-flags-updater.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, Virtue::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, Virtue::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
255     if (player_ptr->riding) {
256         RedrawingFlagsUpdater::get_instance().set_flag(StatusRecalculatingFlag::BONUS);
257     }
258 }
259
260 /*!
261  * @brief パニック・モンスター効果(プレイヤー視界範囲内) / Confuse monsters
262  * @param player_ptr プレイヤーへの参照ポインタ
263  * @param dam 効力
264  * @return 作用が実際にあった場合TRUEを返す
265  */
266 bool confuse_monsters(PlayerType *player_ptr, int dam)
267 {
268     return project_all_los(player_ptr, AttributeType::OLD_CONF, dam);
269 }
270
271 /*!
272  * @brief チャーム・モンスター効果(プレイヤー視界範囲内) / Charm monsters
273  * @param player_ptr プレイヤーへの参照ポインタ
274  * @param dam 効力
275  * @return 作用が実際にあった場合TRUEを返す
276  */
277 bool charm_monsters(PlayerType *player_ptr, int dam)
278 {
279     return project_all_los(player_ptr, AttributeType::CHARM, dam);
280 }
281
282 /*!
283  * @brief 動物魅了効果(プレイヤー視界範囲内) / Charm Animals
284  * @param player_ptr プレイヤーへの参照ポインタ
285  * @param dam 効力
286  * @return 作用が実際にあった場合TRUEを返す
287  */
288 bool charm_animals(PlayerType *player_ptr, int dam)
289 {
290     return project_all_los(player_ptr, AttributeType::CONTROL_ANIMAL, dam);
291 }
292
293 /*!
294  * @brief モンスター朦朧効果(プレイヤー視界範囲内) / Stun monsters
295  * @param player_ptr プレイヤーへの参照ポインタ
296  * @param dam 効力
297  * @return 作用が実際にあった場合TRUEを返す
298  */
299 bool stun_monsters(PlayerType *player_ptr, int dam)
300 {
301     return project_all_los(player_ptr, AttributeType::STUN, dam);
302 }
303
304 /*!
305  * @brief モンスター停止効果(プレイヤー視界範囲内) / Stasis monsters
306  * @param player_ptr プレイヤーへの参照ポインタ
307  * @param dam 効力
308  * @return 作用が実際にあった場合TRUEを返す
309  */
310 bool stasis_monsters(PlayerType *player_ptr, int dam)
311 {
312     return project_all_los(player_ptr, AttributeType::STASIS, dam);
313 }
314
315 /*!
316  * @brief モンスター精神攻撃効果(プレイヤー視界範囲内) / Mindblast monsters
317  * @param player_ptr プレイヤーへの参照ポインタ
318  * @param dam 効力
319  * @return 作用が実際にあった場合TRUEを返す
320  */
321 bool mindblast_monsters(PlayerType *player_ptr, int dam)
322 {
323     return project_all_los(player_ptr, AttributeType::PSI, dam);
324 }
325
326 /*!
327  * @brief モンスター追放効果(プレイヤー視界範囲内) / Banish all monsters
328  * @param player_ptr プレイヤーへの参照ポインタ
329  * @param dist 効力(距離)
330  * @return 作用が実際にあった場合TRUEを返す
331  */
332 bool banish_monsters(PlayerType *player_ptr, int dist)
333 {
334     return project_all_los(player_ptr, AttributeType::AWAY_ALL, dist);
335 }
336
337 /*!
338  * @brief 邪悪退散効果(プレイヤー視界範囲内) / Turn evil
339  * @param player_ptr プレイヤーへの参照ポインタ
340  * @param dam 効力
341  * @return 作用が実際にあった場合TRUEを返す
342  */
343 bool turn_evil(PlayerType *player_ptr, int dam)
344 {
345     return project_all_los(player_ptr, AttributeType::TURN_EVIL, dam);
346 }
347
348 /*!
349  * @brief 全モンスター退散効果(プレイヤー視界範囲内) / Turn everyone
350  * @param player_ptr プレイヤーへの参照ポインタ
351  * @param dam 効力
352  * @return 作用が実際にあった場合TRUEを返す
353  */
354 bool turn_monsters(PlayerType *player_ptr, int dam)
355 {
356     return project_all_los(player_ptr, AttributeType::TURN_ALL, dam);
357 }
358
359 /*!
360  * @brief 死の光線(プレイヤー視界範囲内) / Death-ray all monsters (note: OBSCENELY powerful)
361  * @param player_ptr プレイヤーへの参照ポインタ
362  * @return 作用が実際にあった場合TRUEを返す
363  */
364 bool deathray_monsters(PlayerType *player_ptr)
365 {
366     return project_all_los(player_ptr, AttributeType::DEATH_RAY, player_ptr->lev * 200);
367 }
368
369 /*!
370  * @brief 調査したモンスターの情報を表示する
371  * @param player_ptr プレイヤー情報への参照ポインタ
372  * @param m_ptr モンスター情報への参照ポインタ
373  * @param r_ptr モンスター種族への参照ポインタ
374  * @return 調査結果 善悪アライメント、最大HP、残りHP、AC、速度、ステータス
375  */
376 std::string probed_monster_info(PlayerType *player_ptr, MonsterEntity *m_ptr, MonsterRaceInfo *r_ptr)
377 {
378     if (!m_ptr->is_original_ap()) {
379         if (m_ptr->mflag2.has(MonsterConstantFlagType::KAGE)) {
380             m_ptr->mflag2.reset(MonsterConstantFlagType::KAGE);
381         }
382
383         m_ptr->ap_r_idx = m_ptr->r_idx;
384         lite_spot(player_ptr, m_ptr->fy, m_ptr->fx);
385     }
386
387     const auto m_name = monster_desc(player_ptr, m_ptr, MD_IGNORE_HALLU | MD_INDEF_HIDDEN);
388
389     concptr align;
390     if (r_ptr->kind_flags.has_all_of(alignment_mask)) {
391         align = _("善悪", "good&evil");
392     } else if (r_ptr->kind_flags.has(MonsterKindType::EVIL)) {
393         align = _("邪悪", "evil");
394     } else if (r_ptr->kind_flags.has(MonsterKindType::GOOD)) {
395         align = _("善良", "good");
396     } else if ((m_ptr->sub_align & (SUB_ALIGN_EVIL | SUB_ALIGN_GOOD)) == (SUB_ALIGN_EVIL | SUB_ALIGN_GOOD)) {
397         align = _("中立(善悪)", "neutral(good&evil)");
398     } else if (m_ptr->sub_align & SUB_ALIGN_EVIL) {
399         align = _("中立(邪悪)", "neutral(evil)");
400     } else if (m_ptr->sub_align & SUB_ALIGN_GOOD) {
401         align = _("中立(善良)", "neutral(good)");
402     } else {
403         align = _("中立", "neutral");
404     }
405
406     const auto speed = m_ptr->get_temporary_speed() - STANDARD_SPEED;
407     constexpr auto mes = _("%s ... 属性:%s HP:%d/%d AC:%d 速度:%s%d 経験:", "%s ... align:%s HP:%d/%d AC:%d speed:%s%d exp:");
408     auto result = format(mes, m_name.data(), align, (int)m_ptr->hp, (int)m_ptr->maxhp, r_ptr->ac, (speed > 0) ? "+" : "", speed);
409
410     if (MonsterRace(r_ptr->next_r_idx).is_valid()) {
411         result.append(format("%d/%d ", m_ptr->exp, r_ptr->next_exp));
412     } else {
413         result.append("xxx ");
414     }
415
416     if (m_ptr->is_asleep()) {
417         result.append(_("睡眠 ", "sleeping "));
418     }
419     if (m_ptr->is_stunned()) {
420         result.append(_("朦朧 ", "stunned "));
421     }
422     if (m_ptr->is_fearful()) {
423         result.append(_("恐怖 ", "scared "));
424     }
425     if (m_ptr->is_confused()) {
426         result.append(_("混乱 ", "confused "));
427     }
428     if (m_ptr->is_invulnerable()) {
429         result.append(_("無敵 ", "invulnerable "));
430     }
431     return result;
432 }
433
434 /*!
435  * @brief 周辺モンスターを調査する / Probe nearby monsters
436  * @return 効力があった場合TRUEを返す
437  */
438 bool probing(PlayerType *player_ptr)
439 {
440     bool cu = game_term->scr->cu;
441     bool cv = game_term->scr->cv;
442     game_term->scr->cu = 0;
443     game_term->scr->cv = 1;
444
445     auto &rfu = RedrawingFlagsUpdater::get_instance();
446     auto probe = false;
447     for (int i = 1; i < player_ptr->current_floor_ptr->m_max; i++) {
448         auto *m_ptr = &player_ptr->current_floor_ptr->m_list[i];
449         auto *r_ptr = &monraces_info[m_ptr->r_idx];
450         if (!m_ptr->is_valid()) {
451             continue;
452         }
453         if (!player_has_los_bold(player_ptr, m_ptr->fy, m_ptr->fx)) {
454             continue;
455         }
456         if (!m_ptr->ml) {
457             continue;
458         }
459
460         if (!probe) {
461             msg_print(_("調査中...", "Probing..."));
462         }
463         msg_print(nullptr);
464
465         const auto probe_result = probed_monster_info(player_ptr, m_ptr, r_ptr);
466         prt(probe_result, 0, 0);
467
468         message_add(probe_result);
469         rfu.set_flag(SubWindowRedrawingFlag::MESSAGE);
470         handle_stuff(player_ptr);
471         move_cursor_relative(m_ptr->fy, m_ptr->fx);
472         inkey();
473         term_erase(0, 0);
474         if (lore_do_probe(player_ptr, m_ptr->r_idx)) {
475 #ifdef JP
476             msg_format("%sについてさらに詳しくなった気がする。", r_ptr->name.data());
477 #else
478             std::string nm = r_ptr->name;
479             /* Leave room for making it plural. */
480             nm.resize(r_ptr->name.length() + 16);
481             plural_aux(nm.data());
482             msg_format("You now know more about %s.", nm.data());
483 #endif
484             msg_print(nullptr);
485         }
486
487         probe = true;
488     }
489
490     game_term->scr->cu = cu;
491     game_term->scr->cv = cv;
492     term_fresh();
493
494     if (probe) {
495         chg_virtue(player_ptr, Virtue::KNOWLEDGE, 1);
496         msg_print(_("これで全部です。", "That's all."));
497     }
498
499     return probe;
500 }