OSDN Git Service

[Refactor] QUESTOR を新定義に合わせた
[hengbandforosx/hengbandosx.git] / src / spell-kind / earthquake.cpp
1 #include "spell-kind/earthquake.h"
2 #include "core/window-redrawer.h"
3 #include "dungeon/dungeon-flag-types.h"
4 #include "dungeon/quest.h"
5 #include "floor/cave.h"
6 #include "floor/floor-object.h"
7 #include "floor/geometry.h"
8 #include "game-option/play-record-options.h"
9 #include "game-option/text-display-options.h"
10 #include "grid/feature-flag-types.h"
11 #include "grid/feature.h"
12 #include "grid/grid.h"
13 #include "grid/stair.h"
14 #include "io/write-diary.h"
15 #include "mind/mind-ninja.h"
16 #include "monster-floor/monster-lite.h"
17 #include "monster-race/monster-race.h"
18 #include "monster-race/race-flags1.h"
19 #include "monster-race/race-flags2.h"
20 #include "monster/monster-describer.h"
21 #include "monster/monster-description-types.h"
22 #include "monster/monster-info.h"
23 #include "monster/monster-status-setter.h"
24 #include "monster/monster-update.h"
25 #include "monster/smart-learn-types.h"
26 #include "player/player-damage.h"
27 #include "player/player-move.h"
28 #include "player/player-status-flags.h"
29 #include "player/special-defense-types.h"
30 #include "status/bad-status-setter.h"
31 #include "system/dungeon-info.h"
32 #include "system/floor-type-definition.h"
33 #include "system/grid-type-definition.h"
34 #include "system/monster-entity.h"
35 #include "system/monster-race-info.h"
36 #include "system/player-type-definition.h"
37 #include "system/redrawing-flags-updater.h"
38 #include "system/terrain-type-definition.h"
39 #include "util/bit-flags-calculator.h"
40 #include "view/display-messages.h"
41
42 /*!
43  * @brief 地震処理
44  * Induce an "earthquake" of the given radius at the given location.
45  * @param player_ptrプレイヤーへの参照ポインタ
46  * @param cy 中心Y座標
47  * @param cx 中心X座標
48  * @param r 効果半径
49  * @param m_idx 地震を起こしたモンスターID(0ならばプレイヤー)
50  * @return 効力があった場合TRUEを返す
51  */
52 bool earthquake(PlayerType *player_ptr, POSITION cy, POSITION cx, POSITION r, MONSTER_IDX m_idx)
53 {
54     auto *floor_ptr = player_ptr->current_floor_ptr;
55     if ((floor_ptr->is_in_quest() && QuestType::is_fixed(floor_ptr->quest_number)) || !floor_ptr->dun_level) {
56         return false;
57     }
58
59     if (r > 12) {
60         r = 12;
61     }
62
63     bool map[32][32]{};
64     for (auto y = 0; y < 32; y++) {
65         for (auto x = 0; x < 32; x++) {
66             map[y][x] = false;
67         }
68     }
69
70     auto damage = 0;
71     auto hurt = false;
72     for (auto dy = -r; dy <= r; dy++) {
73         for (auto dx = -r; dx <= r; dx++) {
74             const Pos2D pos(cy + dy, cx + dx);
75             if (!in_bounds(floor_ptr, pos.y, pos.x)) {
76                 continue;
77             }
78
79             if (distance(cy, cx, pos.y, pos.x) > r) {
80                 continue;
81             }
82
83             auto &grid = floor_ptr->get_grid(pos);
84             grid.info &= ~(CAVE_ROOM | CAVE_ICKY | CAVE_UNSAFE);
85             grid.info &= ~(CAVE_GLOW | CAVE_MARK | CAVE_KNOWN);
86             if (!dx && !dy) {
87                 continue;
88             }
89
90             if (randint0(100) < 85) {
91                 continue;
92             }
93
94             map[16 + pos.y - cy][16 + pos.x - cx] = true;
95             if (player_ptr->is_located_at(pos)) {
96                 hurt = true;
97             }
98         }
99     }
100
101     auto sn = 0;
102     Pos2D p_pos_new(0, 0); // 落石を避けた後のプレイヤー座標
103     if (hurt && !has_pass_wall(player_ptr) && !has_kill_wall(player_ptr)) {
104         for (auto i = 0; i < 8; i++) {
105             auto y = player_ptr->y + ddy_ddd[i];
106             auto x = player_ptr->x + ddx_ddd[i];
107             if (!is_cave_empty_bold(player_ptr, y, x)) {
108                 continue;
109             }
110
111             if (map[16 + y - cy][16 + x - cx]) {
112                 continue;
113             }
114
115             if (floor_ptr->grid_array[y][x].m_idx) {
116                 continue;
117             }
118
119             sn++;
120             if (randint0(sn) > 0) {
121                 continue;
122             }
123
124             p_pos_new = { y, x };
125         }
126
127         constexpr static auto msgs = {
128             _("ダンジョンの壁が崩れた!", "The dungeon's ceiling collapses!"),
129             _("ダンジョンの床が不自然にねじ曲がった!", "The dungeon's floor twists in an unnatural way!"),
130             _("ダンジョンが揺れた!崩れた岩が頭に降ってきた!", "The dungeon quakes!  You are pummeled with debris!"),
131         };
132         msg_print(rand_choice(msgs));
133
134         if (!sn) {
135             msg_print(_("あなたはひどい怪我を負った!", "You are severely crushed!"));
136             damage = 200;
137         } else {
138             constexpr std::array<std::pair<bool, std::string_view>, 3> candidates = { {
139                 { false, _("降り注ぐ岩をうまく避けた!", "You nimbly dodge the blast!") },
140                 { true, _("岩石があなたに直撃した!", "You are bashed by rubble!") },
141                 { true, _("あなたは床と壁との間に挟まれてしまった!", "You are crushed between the floor and ceiling!") },
142             } };
143
144             const auto &[is_damaged, msg] = rand_choice(candidates);
145
146             msg_print(msg);
147             if (is_damaged) {
148                 damage = damroll(10, 4);
149                 BadStatusSetter(player_ptr).mod_stun(randint1(50));
150             }
151
152             (void)move_player_effect(player_ptr, p_pos_new.y, p_pos_new.x, MPE_DONT_PICKUP);
153         }
154
155         map[16 + player_ptr->y - cy][16 + player_ptr->x - cx] = false;
156         if (damage) {
157             std::string killer;
158
159             if (m_idx) {
160                 auto *m_ptr = &floor_ptr->m_list[m_idx];
161                 const auto m_name = monster_desc(player_ptr, m_ptr, MD_WRONGDOER_NAME);
162                 killer = format(_("%sの起こした地震", "an earthquake caused by %s"), m_name.data());
163             } else {
164                 killer = _("地震", "an earthquake");
165             }
166
167             take_hit(player_ptr, DAMAGE_ATTACK, damage, killer);
168         }
169     }
170
171     for (auto dy = -r; dy <= r; dy++) {
172         for (auto dx = -r; dx <= r; dx++) {
173             const Pos2D pos(cy + dy, cx + dx);
174             if (!map[16 + pos.y - cy][16 + pos.x - cx]) {
175                 continue;
176             }
177
178             auto &grid = floor_ptr->get_grid(pos);
179             if (grid.m_idx == player_ptr->riding) {
180                 continue;
181             }
182
183             if (!grid.m_idx) {
184                 continue;
185             }
186
187             auto *m_ptr = &floor_ptr->m_list[grid.m_idx];
188             auto *r_ptr = &m_ptr->get_monrace();
189             if (r_ptr->misc_flags.has(MonsterMiscType::QUESTOR)) {
190                 map[16 + pos.y - cy][16 + pos.x - cx] = false;
191                 continue;
192             }
193
194             if (r_ptr->feature_flags.has(MonsterFeatureType::KILL_WALL) || r_ptr->feature_flags.has(MonsterFeatureType::PASS_WALL)) {
195                 continue;
196             }
197
198             sn = 0;
199             if (r_ptr->behavior_flags.has_not(MonsterBehaviorType::NEVER_MOVE)) {
200                 for (DIRECTION i = 0; i < 8; i++) {
201                     const Pos2D pos_neighbor(pos.y + ddy_ddd[i], pos.x + ddx_ddd[i]);
202                     if (!is_cave_empty_bold(player_ptr, pos_neighbor.y, pos_neighbor.x)) {
203                         continue;
204                     }
205
206                     const auto &grid_neighbor = floor_ptr->get_grid(pos_neighbor);
207                     if (grid_neighbor.is_rune_protection()) {
208                         continue;
209                     }
210
211                     if (grid_neighbor.is_rune_explosion()) {
212                         continue;
213                     }
214
215                     if (pattern_tile(floor_ptr, pos_neighbor.y, pos_neighbor.x)) {
216                         continue;
217                     }
218
219                     if (map[16 + pos_neighbor.y - cy][16 + pos_neighbor.x - cx]) {
220                         continue;
221                     }
222
223                     if (grid_neighbor.m_idx) {
224                         continue;
225                     }
226
227                     if (player_ptr->is_located_at(pos)) {
228                         continue;
229                     }
230
231                     sn++;
232
233                     if (randint0(sn) > 0) {
234                         continue;
235                     }
236
237                     p_pos_new = pos_neighbor;
238                 }
239             }
240
241             const auto m_name = monster_desc(player_ptr, m_ptr, 0);
242             if (!ignore_unview || is_seen(player_ptr, m_ptr)) {
243                 msg_format(_("%s^は苦痛で泣きわめいた!", "%s^ wails out in pain!"), m_name.data());
244             }
245
246             damage = (sn ? damroll(4, 8) : (m_ptr->hp + 1));
247             (void)set_monster_csleep(player_ptr, grid.m_idx, 0);
248             m_ptr->hp -= damage;
249             if (m_ptr->hp < 0) {
250                 if (!ignore_unview || is_seen(player_ptr, m_ptr)) {
251                     msg_format(_("%s^は岩石に埋もれてしまった!", "%s^ is embedded in the rock!"), m_name.data());
252                 }
253
254                 if (grid.m_idx) {
255                     const auto &m_ref = floor_ptr->m_list[grid.m_idx];
256                     if (record_named_pet && m_ref.is_named_pet()) {
257                         const auto m2_name = monster_desc(player_ptr, m_ptr, MD_INDEF_VISIBLE);
258                         exe_write_diary(player_ptr, DiaryKind::NAMED_PET, RECORD_NAMED_PET_EARTHQUAKE, m2_name);
259                     }
260                 }
261
262                 delete_monster(player_ptr, pos.y, pos.x);
263                 sn = 0;
264             }
265
266             if (sn == 0) {
267                 continue;
268             }
269
270             const auto m_idx_aux = grid.m_idx;
271             grid.m_idx = 0;
272             floor_ptr->get_grid(p_pos_new).m_idx = m_idx_aux;
273             m_ptr->fy = p_pos_new.y;
274             m_ptr->fx = p_pos_new.x;
275             update_monster(player_ptr, m_idx_aux, true);
276             lite_spot(player_ptr, pos.y, pos.x);
277             lite_spot(player_ptr, p_pos_new.y, p_pos_new.x);
278         }
279     }
280
281     clear_mon_lite(floor_ptr);
282     for (auto dy = -r; dy <= r; dy++) {
283         for (auto dx = -r; dx <= r; dx++) {
284             auto yy = cy + dy;
285             auto xx = cx + dx;
286             if (!map[16 + yy - cy][16 + xx - cx]) {
287                 continue;
288             }
289
290             if (!cave_valid_bold(floor_ptr, yy, xx)) {
291                 continue;
292             }
293
294             delete_all_items_from_floor(player_ptr, yy, xx);
295             int t = cave_has_flag_bold(floor_ptr, yy, xx, TerrainCharacteristics::PROJECT) ? randint0(100) : 200;
296             if (t < 20) {
297                 cave_set_feat(player_ptr, yy, xx, feat_granite);
298                 continue;
299             }
300
301             if (t < 70) {
302                 cave_set_feat(player_ptr, yy, xx, feat_quartz_vein);
303                 continue;
304             }
305
306             if (t < 100) {
307                 cave_set_feat(player_ptr, yy, xx, feat_magma_vein);
308                 continue;
309             }
310
311             cave_set_feat(player_ptr, yy, xx, rand_choice(feat_ground_type));
312         }
313     }
314
315     for (auto dy = -r; dy <= r; dy++) {
316         for (auto dx = -r; dx <= r; dx++) {
317             const Pos2D pos(cy + dy, cx + dx);
318             if (!in_bounds(floor_ptr, pos.y, pos.x)) {
319                 continue;
320             }
321
322             if (distance(cy, cx, pos.y, pos.x) > r) {
323                 continue;
324             }
325
326             auto &grid = floor_ptr->get_grid(pos);
327             if (grid.is_mirror()) {
328                 grid.info |= CAVE_GLOW;
329                 continue;
330             }
331
332             if (floor_ptr->get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
333                 continue;
334             }
335
336             for (auto j = 0; j < 9; j++) {
337                 const Pos2D pos_neighbor(pos.y + ddy_ddd[j], pos.x + ddx_ddd[j]);
338                 if (!in_bounds2(floor_ptr, pos_neighbor.y, pos_neighbor.x)) {
339                     continue;
340                 }
341
342                 const auto &grid_neighbor = floor_ptr->get_grid(pos_neighbor);
343                 if (grid_neighbor.get_terrain_mimic().flags.has(TerrainCharacteristics::GLOW)) {
344                     grid.info |= CAVE_GLOW;
345                     break;
346                 }
347             }
348         }
349     }
350
351     auto &rfu = RedrawingFlagsUpdater::get_instance();
352     static constexpr auto flags_srf = {
353         StatusRecalculatingFlag::UN_VIEW,
354         StatusRecalculatingFlag::UN_LITE,
355         StatusRecalculatingFlag::VIEW,
356         StatusRecalculatingFlag::LITE,
357         StatusRecalculatingFlag::FLOW,
358         StatusRecalculatingFlag::MONSTER_LITE,
359         StatusRecalculatingFlag::MONSTER_STATUSES,
360     };
361     rfu.set_flags(flags_srf);
362     static constexpr auto flags_mwrf = {
363         MainWindowRedrawingFlag::HEALTH,
364         MainWindowRedrawingFlag::UHEALTH,
365         MainWindowRedrawingFlag::MAP,
366     };
367     rfu.set_flags(flags_mwrf);
368     static constexpr auto flags_swrf = {
369         SubWindowRedrawingFlag::OVERHEAD,
370         SubWindowRedrawingFlag::DUNGEON,
371     };
372     rfu.set_flags(flags_swrf);
373     if (floor_ptr->grid_array[player_ptr->y][player_ptr->x].info & CAVE_GLOW) {
374         set_superstealth(player_ptr, false);
375     }
376
377     return true;
378 }