OSDN Git Service

Merge pull request #3814 from Slimebreath6078/feature/Add_Laffey_II
[hengbandforosx/hengbandosx.git] / src / spell-realm / spells-crusade.cpp
1 /*!
2  * @brief 破邪魔法処理
3  * @date 2020/06/05
4  * @author Hourier
5  */
6
7 #include "spell-realm/spells-crusade.h"
8 #include "core/disturbance.h"
9 #include "core/stuff-handler.h"
10 #include "effect/attribute-types.h"
11 #include "effect/effect-characteristics.h"
12 #include "effect/effect-processor.h"
13 #include "floor/cave.h"
14 #include "floor/geometry.h"
15 #include "game-option/disturbance-options.h"
16 #include "grid/feature-flag-types.h"
17 #include "spell-realm/spells-crusade.h"
18 #include "spell/range-calc.h"
19 #include "system/angband-system.h"
20 #include "system/floor-type-definition.h"
21 #include "system/grid-type-definition.h"
22 #include "system/player-type-definition.h"
23 #include "system/redrawing-flags-updater.h"
24 #include "target/projection-path-calculator.h"
25 #include "target/target-checker.h"
26 #include "target/target-getter.h"
27 #include "util/bit-flags-calculator.h"
28 #include "view/display-messages.h"
29
30 /*!
31  * @brief 破邪魔法「神の怒り」の処理としてターゲットを指定した後分解のボールを最大20回発生させる。
32  * @param player_ptr プレイヤーへの参照ポインタ
33  * @param dam ダメージ
34  * @param rad 効力の半径
35  * @return ターゲットを指定し、実行したならばTRUEを返す。
36  */
37 bool cast_wrath_of_the_god(PlayerType *player_ptr, int dam, POSITION rad)
38 {
39     int dir;
40     if (!get_aim_dir(player_ptr, &dir)) {
41         return false;
42     }
43
44     Pos2D pos_target(player_ptr->y + 99 * ddy[dir], player_ptr->x + 99 * ddx[dir]);
45     if ((dir == 5) && target_okay(player_ptr)) {
46         pos_target.x = target_col;
47         pos_target.y = target_row;
48     }
49
50     Pos2D pos = player_ptr->get_position();
51     auto &floor = *player_ptr->current_floor_ptr;
52     while (true) {
53         if (pos == pos_target) {
54             break;
55         }
56
57         const auto pos_to = mmove2(pos, player_ptr->get_position(), pos_target);
58         if (AngbandSystem::get_instance().get_max_range() <= distance(player_ptr->y, player_ptr->x, pos_to.y, pos_to.x)) {
59             break;
60         }
61         if (!cave_has_flag_bold(&floor, pos_to.y, pos_to.x, TerrainCharacteristics::PROJECT)) {
62             break;
63         }
64         if ((dir != 5) && floor.get_grid(pos_to).has_monster()) {
65             break;
66         }
67
68         pos = pos_to;
69     }
70
71     pos_target = pos;
72     const auto b = 10 + randint1(10);
73     for (auto i = 0; i < b; i++) {
74         auto count = 20;
75         Pos2D pos_explode(pos_target.x, pos_target.y);
76         while (count--) {
77             const auto x = pos_target.x - 5 + randint0(11);
78             const auto y = pos_target.y - 5 + randint0(11);
79             const auto dx = (pos_target.x > x) ? (pos_target.x - x) : (x - pos_target.x);
80             const auto dy = (pos_target.y > y) ? (pos_target.y - y) : (y - pos_target.y);
81             const auto d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
82             if (d < 5) {
83                 pos_explode.x = x;
84                 pos_explode.y = y;
85                 break;
86             }
87         }
88
89         if (count < 0) {
90             continue;
91         }
92
93         auto should_cast = in_bounds(&floor, pos_explode.y, pos_explode.x);
94         should_cast &= !cave_stop_disintegration(&floor, pos_explode.y, pos_explode.x);
95         should_cast &= in_disintegration_range(&floor, pos_target.y, pos_target.x, pos_explode.y, pos_explode.x);
96         if (!should_cast) {
97             continue;
98         }
99
100         constexpr auto mode = PROJECT_JUMP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
101         project(player_ptr, 0, rad, pos_explode.y, pos_explode.x, dam, AttributeType::DISINTEGRATE, mode);
102     }
103
104     return true;
105 }
106
107 /*!
108  * @brief 一時的聖なるのオーラの継続時間をセットする / Set "tim_sh_holy", notice observable changes
109  * @param v 継続時間
110  * @param do_dec 現在の継続時間より長い値のみ上書きする
111  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す。
112  */
113 bool set_tim_sh_holy(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
114 {
115     bool notice = false;
116     v = (v > 10000) ? 10000 : (v < 0) ? 0
117                                       : v;
118
119     if (player_ptr->is_dead) {
120         return false;
121     }
122
123     if (v) {
124         if (player_ptr->tim_sh_holy && !do_dec) {
125             if (player_ptr->tim_sh_holy > v) {
126                 return false;
127             }
128         } else if (!player_ptr->tim_sh_holy) {
129             msg_print(_("体が聖なるオーラで覆われた。", "You are enveloped by a holy aura!"));
130             notice = true;
131         }
132     } else {
133         if (player_ptr->tim_sh_holy) {
134             msg_print(_("聖なるオーラが消えた。", "The holy aura disappeared."));
135             notice = true;
136         }
137     }
138
139     auto &rfu = RedrawingFlagsUpdater::get_instance();
140     player_ptr->tim_sh_holy = v;
141     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
142
143     if (!notice) {
144         return false;
145     }
146
147     if (disturb_state) {
148         disturb(player_ptr, false, false);
149     }
150
151     rfu.set_flag(StatusRecalculatingFlag::BONUS);
152     handle_stuff(player_ptr);
153     return true;
154 }
155
156 /*!
157  * @brief 目には目をの残り時間をセットする / Set "tim_eyeeye", notice observable changes
158  * @param v 継続時間
159  * @param do_dec 現在の継続時間より長い値のみ上書きする
160  * @return ステータスに影響を及ぼす変化があった場合TRUEを返す
161  * @details 呪術領域でも使えるが、汎用性と行数の兼ね合いを考えて破邪側に入れた
162  */
163 bool set_tim_eyeeye(PlayerType *player_ptr, TIME_EFFECT v, bool do_dec)
164 {
165     bool notice = false;
166     v = (v > 10000) ? 10000 : (v < 0) ? 0
167                                       : v;
168
169     if (player_ptr->is_dead) {
170         return false;
171     }
172
173     if (v) {
174         if (player_ptr->tim_eyeeye && !do_dec) {
175             if (player_ptr->tim_eyeeye > v) {
176                 return false;
177             }
178         } else if (!player_ptr->tim_eyeeye) {
179             msg_print(_("法の守り手になった気がした!", "You feel like a keeper of commandments!"));
180             notice = true;
181         }
182     } else {
183         if (player_ptr->tim_eyeeye) {
184             msg_print(_("懲罰を執行することができなくなった。", "You lost your aura of retribution."));
185             notice = true;
186         }
187     }
188
189     auto &rfu = RedrawingFlagsUpdater::get_instance();
190     player_ptr->tim_eyeeye = v;
191     rfu.set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
192
193     if (!notice) {
194         return false;
195     }
196
197     if (disturb_state) {
198         disturb(player_ptr, false, false);
199     }
200
201     rfu.set_flag(StatusRecalculatingFlag::BONUS);
202     handle_stuff(player_ptr);
203     return true;
204 }