OSDN Git Service

[Refactor] monster_idxと0との比較を関数化する
[hengbandforosx/hengbandosx.git] / src / spell-realm / spells-chaos.cpp
1 #include "spell-realm/spells-chaos.h"
2 #include "core/window-redrawer.h"
3 #include "dungeon/quest.h"
4 #include "effect/attribute-types.h"
5 #include "effect/effect-characteristics.h"
6 #include "effect/effect-processor.h"
7 #include "floor/cave.h"
8 #include "floor/geometry.h"
9 #include "grid/feature.h"
10 #include "grid/grid.h"
11 #include "monster/monster-describer.h"
12 #include "monster/monster-status-setter.h"
13 #include "monster/monster-status.h"
14 #include "monster/monster-util.h"
15 #include "player-info/class-info.h"
16 #include "player/player-damage.h"
17 #include "spell-kind/spells-floor.h"
18 #include "spell-kind/spells-launcher.h"
19 #include "system/floor-type-definition.h"
20 #include "system/grid-type-definition.h"
21 #include "system/monster-entity.h"
22 #include "system/player-type-definition.h"
23 #include "system/redrawing-flags-updater.h"
24 #include "system/terrain-type-definition.h"
25 #include "target/projection-path-calculator.h"
26 #include "util/bit-flags-calculator.h"
27 #include "view/display-messages.h"
28
29 /*!
30  * @brief 虚無招来処理 /
31  * @param player_ptr プレイヤーへの参照ポインタ
32  * @details
33  * Sorry, it becomes not (void)...
34  */
35 void call_the_void(PlayerType *player_ptr)
36 {
37     auto do_call = true;
38     const auto &floor = *player_ptr->current_floor_ptr;
39     for (int i = 0; i < 9; i++) {
40         const Pos2D p_pos_neighbor(player_ptr->y + ddy_ddd[i], player_ptr->x + ddx_ddd[i]);
41         const auto &grid = floor.get_grid(p_pos_neighbor);
42         if (!grid.cave_has_flag(TerrainCharacteristics::PROJECT)) {
43             if (!grid.mimic || grid.get_terrain_mimic_raw().flags.has_not(TerrainCharacteristics::PROJECT) || !grid.get_terrain().is_permanent_wall()) {
44                 do_call = false;
45                 break;
46             }
47         }
48     }
49
50     if (do_call) {
51         for (int i = 1; i < 10; i++) {
52             if (i - 5) {
53                 fire_ball(player_ptr, AttributeType::ROCKET, i, 175, 2);
54             }
55         }
56
57         for (int i = 1; i < 10; i++) {
58             if (i - 5) {
59                 fire_ball(player_ptr, AttributeType::MANA, i, 175, 3);
60             }
61         }
62
63         for (int i = 1; i < 10; i++) {
64             if (i - 5) {
65                 fire_ball(player_ptr, AttributeType::NUKE, i, 175, 4);
66             }
67         }
68
69         return;
70     }
71
72     auto is_special_fllor = floor.is_in_quest() && QuestType::is_fixed(floor.quest_number);
73     is_special_fllor |= floor.dun_level == 0;
74     if (is_special_fllor) {
75         msg_print(_("地面が揺れた。", "The ground trembles."));
76         return;
77     }
78
79 #ifdef JP
80     msg_format("あなたは%sを壁に近すぎる場所で唱えてしまった!", ((mp_ptr->spell_book == ItemKindType::LIFE_BOOK) ? "祈り" : "呪文"));
81 #else
82     msg_format("You %s the %s too close to a wall!", ((mp_ptr->spell_book == ItemKindType::LIFE_BOOK) ? "recite" : "cast"),
83         ((mp_ptr->spell_book == ItemKindType::LIFE_BOOK) ? "prayer" : "spell"));
84 #endif
85     msg_print(_("大きな爆発音があった!", "There is a loud explosion!"));
86
87     if (one_in_(666)) {
88         if (!vanish_dungeon(player_ptr)) {
89             msg_print(_("ダンジョンは一瞬静まり返った。", "The dungeon becomes quiet for a moment."));
90         }
91         take_hit(player_ptr, DAMAGE_NOESCAPE, 100 + randint1(150), _("自殺的な虚無招来", "a suicidal Call the Void"));
92         return;
93     }
94
95     if (destroy_area(player_ptr, player_ptr->y, player_ptr->x, 15 + player_ptr->lev + randint0(11), false)) {
96         msg_print(_("ダンジョンが崩壊した...", "The dungeon collapses..."));
97     } else {
98         msg_print(_("ダンジョンは大きく揺れた。", "The dungeon trembles."));
99     }
100     take_hit(player_ptr, DAMAGE_NOESCAPE, 100 + randint1(150), _("自殺的な虚無招来", "a suicidal Call the Void"));
101 }
102
103 /*!
104  * @brief 与えられたグリッドを壁から床にする
105  * @param floor フロアへの参照
106  * @param pos グリッドの座標
107  * @todo FloorType/Grid のオブジェクトメソッドへ繰り込む
108  */
109 static void erase_wall(FloorType &floor, const Pos2D &pos)
110 {
111     auto &grid = floor.get_grid(pos);
112     const auto &terrain = grid.get_terrain_mimic_raw();
113     grid.info &= ~(CAVE_ROOM | CAVE_ICKY);
114     if ((grid.mimic == 0) || terrain.flags.has_not(TerrainCharacteristics::HURT_DISI)) {
115         return;
116     }
117
118     grid.mimic = feat_state(&floor, grid.mimic, TerrainCharacteristics::HURT_DISI);
119     const auto &terrain_changed = grid.get_terrain_mimic_raw();
120     if (terrain_changed.flags.has_not(TerrainCharacteristics::REMEMBER)) {
121         grid.info &= ~(CAVE_MARK);
122     }
123 }
124
125 /*!
126  * @brief フロアの全てを床にする
127  * @param floor フロアへの参照
128  * @todo FloorType のオブジェクトメソッドへ繰り込む
129  */
130 static void erase_all_walls(FloorType &floor)
131 {
132     for (auto x = 0; x < floor.width; x++) {
133         erase_wall(floor, { 0, x });
134         erase_wall(floor, { floor.height - 1, x });
135     }
136
137     for (auto y = 1; y < (floor.height - 1); y++) {
138         erase_wall(floor, { y, 0 });
139         erase_wall(floor, { y, floor.width - 1 });
140     }
141 }
142
143 /*!
144  * @brief 虚無招来によるフロア中の全壁除去処理 /
145  * Vanish all walls in this floor
146  * @param player_ptr プレイヤーへの参照ポインタ
147  * @param player_ptr 術者の参照ポインタ
148  * @return 実際に処理が反映された場合TRUE
149  */
150 bool vanish_dungeon(PlayerType *player_ptr)
151 {
152     auto &floor = *player_ptr->current_floor_ptr;
153     auto is_special_floor = floor.is_in_quest() && QuestType::is_fixed(floor.quest_number);
154     is_special_floor |= (floor.dun_level == 0);
155     if (is_special_floor) {
156         return false;
157     }
158
159     for (auto y = 1; y < floor.height - 1; y++) {
160         for (auto x = 1; x < floor.width - 1; x++) {
161             const Pos2D pos(y, x);
162             auto &grid = floor.get_grid(pos);
163             const auto &terrrain = grid.get_terrain();
164             grid.info &= ~(CAVE_ROOM | CAVE_ICKY);
165             const auto &monster = floor.m_list[grid.m_idx];
166             if (is_monster(grid.m_idx) && monster.is_asleep()) {
167                 (void)set_monster_csleep(player_ptr, grid.m_idx, 0);
168                 if (monster.ml) {
169                     const auto m_name = monster_desc(player_ptr, &monster, 0);
170                     msg_format(_("%s^が目を覚ました。", "%s^ wakes up."), m_name.data());
171                 }
172             }
173
174             if (terrrain.flags.has(TerrainCharacteristics::HURT_DISI)) {
175                 cave_alter_feat(player_ptr, y, x, TerrainCharacteristics::HURT_DISI);
176             }
177         }
178     }
179
180     erase_all_walls(floor);
181     auto &rfu = RedrawingFlagsUpdater::get_instance();
182     static constexpr auto flags_srf = {
183         StatusRecalculatingFlag::UN_VIEW,
184         StatusRecalculatingFlag::UN_LITE,
185         StatusRecalculatingFlag::VIEW,
186         StatusRecalculatingFlag::LITE,
187         StatusRecalculatingFlag::FLOW,
188         StatusRecalculatingFlag::MONSTER_LITE,
189         StatusRecalculatingFlag::MONSTER_STATUSES,
190     };
191     rfu.set_flags(flags_srf);
192     rfu.set_flag(MainWindowRedrawingFlag::MAP);
193     static constexpr auto flags_swrf = {
194         SubWindowRedrawingFlag::OVERHEAD,
195         SubWindowRedrawingFlag::DUNGEON,
196     };
197     rfu.set_flags(flags_swrf);
198     return true;
199 }
200
201 /*!
202  * @brief カオス魔法「流星群」/トランプ魔法「隕石のカード」の処理としてプレイヤーを中心に隕石落下処理を10+1d10回繰り返す。
203  * / Drop 10+1d10 meteor ball at random places near the player
204  * @param player_ptr プレイヤーへの参照ポインタ
205  * @param dam ダメージ
206  * @param rad 効力の半径
207  * @details このファイルにいるのは、spells-trump.c と比べて行数が少なかったため。それ以上の意図はない
208  */
209 void cast_meteor(PlayerType *player_ptr, int dam, POSITION rad)
210 {
211     int b = 10 + randint1(10);
212     for (int i = 0; i < b; i++) {
213         POSITION y = 0, x = 0;
214         int count;
215
216         for (count = 0; count <= 20; count++) {
217             int dy, dx, d;
218
219             x = player_ptr->x - 8 + randint0(17);
220             y = player_ptr->y - 8 + randint0(17);
221             dx = (player_ptr->x > x) ? (player_ptr->x - x) : (x - player_ptr->x);
222             dy = (player_ptr->y > y) ? (player_ptr->y - y) : (y - player_ptr->y);
223             d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
224
225             if (d >= 9) {
226                 continue;
227             }
228
229             auto *floor_ptr = player_ptr->current_floor_ptr;
230             if (!in_bounds(floor_ptr, y, x)) {
231                 continue;
232             }
233
234             const auto is_projectable = projectable(player_ptr, player_ptr->y, player_ptr->x, y, x);
235             if (!is_projectable || !cave_has_flag_bold(floor_ptr, y, x, TerrainCharacteristics::PROJECT)) {
236                 continue;
237             }
238
239             break;
240         }
241
242         if (count > 20) {
243             continue;
244         }
245
246         project(player_ptr, 0, rad, y, x, dam, AttributeType::METEOR, PROJECT_KILL | PROJECT_JUMP | PROJECT_ITEM);
247     }
248 }