OSDN Git Service

Merge pull request #3664 from Hourier/release/3.0.0.90-Alpha
[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 (POSITION y = 0; y < 32; y++) {
65         for (POSITION x = 0; x < 32; x++) {
66             map[y][x] = false;
67         }
68     }
69
70     int damage = 0;
71     bool hurt = false;
72     for (POSITION dy = -r; dy <= r; dy++) {
73         for (POSITION dx = -r; dx <= r; dx++) {
74             POSITION yy = cy + dy;
75             POSITION xx = cx + dx;
76
77             if (!in_bounds(floor_ptr, yy, xx)) {
78                 continue;
79             }
80
81             if (distance(cy, cx, yy, xx) > r) {
82                 continue;
83             }
84
85             grid_type *g_ptr;
86             g_ptr = &floor_ptr->grid_array[yy][xx];
87             g_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY | CAVE_UNSAFE);
88             g_ptr->info &= ~(CAVE_GLOW | CAVE_MARK | CAVE_KNOWN);
89             if (!dx && !dy) {
90                 continue;
91             }
92
93             if (randint0(100) < 85) {
94                 continue;
95             }
96
97             map[16 + yy - cy][16 + xx - cx] = true;
98             if (player_bold(player_ptr, yy, xx)) {
99                 hurt = true;
100             }
101         }
102     }
103
104     int sn = 0;
105     POSITION sy = 0, sx = 0;
106     if (hurt && !has_pass_wall(player_ptr) && !has_kill_wall(player_ptr)) {
107         for (DIRECTION i = 0; i < 8; i++) {
108             POSITION y = player_ptr->y + ddy_ddd[i];
109             POSITION x = player_ptr->x + ddx_ddd[i];
110             if (!is_cave_empty_bold(player_ptr, y, x)) {
111                 continue;
112             }
113
114             if (map[16 + y - cy][16 + x - cx]) {
115                 continue;
116             }
117
118             if (floor_ptr->grid_array[y][x].m_idx) {
119                 continue;
120             }
121
122             sn++;
123             if (randint0(sn) > 0) {
124                 continue;
125             }
126
127             sy = y;
128             sx = x;
129         }
130
131         constexpr static auto msgs = {
132             _("ダンジョンの壁が崩れた!", "The dungeon's ceiling collapses!"),
133             _("ダンジョンの床が不自然にねじ曲がった!", "The dungeon's floor twists in an unnatural way!"),
134             _("ダンジョンが揺れた!崩れた岩が頭に降ってきた!", "The dungeon quakes!  You are pummeled with debris!"),
135         };
136         msg_print(rand_choice(msgs));
137
138         if (!sn) {
139             msg_print(_("あなたはひどい怪我を負った!", "You are severely crushed!"));
140             damage = 200;
141         } else {
142             constexpr std::array<std::pair<bool, std::string_view>, 3> candidates = { {
143                 { false, _("降り注ぐ岩をうまく避けた!", "You nimbly dodge the blast!") },
144                 { true, _("岩石があなたに直撃した!", "You are bashed by rubble!") },
145                 { true, _("あなたは床と壁との間に挟まれてしまった!", "You are crushed between the floor and ceiling!") },
146             } };
147
148             const auto &[is_damaged, msg] = rand_choice(candidates);
149
150             msg_print(msg);
151             if (is_damaged) {
152                 damage = damroll(10, 4);
153                 BadStatusSetter(player_ptr).mod_stun(randint1(50));
154             }
155
156             (void)move_player_effect(player_ptr, sy, sx, MPE_DONT_PICKUP);
157         }
158
159         map[16 + player_ptr->y - cy][16 + player_ptr->x - cx] = false;
160         if (damage) {
161             std::string killer;
162
163             if (m_idx) {
164                 auto *m_ptr = &floor_ptr->m_list[m_idx];
165                 const auto m_name = monster_desc(player_ptr, m_ptr, MD_WRONGDOER_NAME);
166                 killer = format(_("%sの起こした地震", "an earthquake caused by %s"), m_name.data());
167             } else {
168                 killer = _("地震", "an earthquake");
169             }
170
171             take_hit(player_ptr, DAMAGE_ATTACK, damage, killer);
172         }
173     }
174
175     for (POSITION dy = -r; dy <= r; dy++) {
176         for (POSITION dx = -r; dx <= r; dx++) {
177             POSITION yy = cy + dy;
178             POSITION xx = cx + dx;
179             if (!map[16 + yy - cy][16 + xx - cx]) {
180                 continue;
181             }
182
183             grid_type *gg_ptr;
184             gg_ptr = &floor_ptr->grid_array[yy][xx];
185             if (gg_ptr->m_idx == player_ptr->riding) {
186                 continue;
187             }
188
189             if (!gg_ptr->m_idx) {
190                 continue;
191             }
192
193             auto *m_ptr = &floor_ptr->m_list[gg_ptr->m_idx];
194             auto *r_ptr = &m_ptr->get_monrace();
195             if (r_ptr->flags1 & RF1_QUESTOR) {
196                 map[16 + yy - cy][16 + xx - cx] = false;
197                 continue;
198             }
199
200             if (r_ptr->feature_flags.has(MonsterFeatureType::KILL_WALL) || r_ptr->feature_flags.has(MonsterFeatureType::PASS_WALL)) {
201                 continue;
202             }
203
204             sn = 0;
205             if (r_ptr->behavior_flags.has_not(MonsterBehaviorType::NEVER_MOVE)) {
206                 for (DIRECTION i = 0; i < 8; i++) {
207                     POSITION y = yy + ddy_ddd[i];
208                     POSITION x = xx + ddx_ddd[i];
209                     if (!is_cave_empty_bold(player_ptr, y, x)) {
210                         continue;
211                     }
212
213                     auto *g_ptr = &floor_ptr->grid_array[y][x];
214                     if (g_ptr->is_rune_protection()) {
215                         continue;
216                     }
217
218                     if (g_ptr->is_rune_explosion()) {
219                         continue;
220                     }
221
222                     if (pattern_tile(floor_ptr, y, x)) {
223                         continue;
224                     }
225
226                     if (map[16 + y - cy][16 + x - cx]) {
227                         continue;
228                     }
229
230                     if (floor_ptr->grid_array[y][x].m_idx) {
231                         continue;
232                     }
233
234                     if (player_bold(player_ptr, y, x)) {
235                         continue;
236                     }
237
238                     sn++;
239
240                     if (randint0(sn) > 0) {
241                         continue;
242                     }
243
244                     sy = y;
245                     sx = x;
246                 }
247             }
248
249             const auto m_name = monster_desc(player_ptr, m_ptr, 0);
250             if (!ignore_unview || is_seen(player_ptr, m_ptr)) {
251                 msg_format(_("%s^は苦痛で泣きわめいた!", "%s^ wails out in pain!"), m_name.data());
252             }
253
254             damage = (sn ? damroll(4, 8) : (m_ptr->hp + 1));
255             (void)set_monster_csleep(player_ptr, gg_ptr->m_idx, 0);
256             m_ptr->hp -= damage;
257             if (m_ptr->hp < 0) {
258                 if (!ignore_unview || is_seen(player_ptr, m_ptr)) {
259                     msg_format(_("%s^は岩石に埋もれてしまった!", "%s^ is embedded in the rock!"), m_name.data());
260                 }
261
262                 if (gg_ptr->m_idx) {
263                     const auto &m_ref = floor_ptr->m_list[gg_ptr->m_idx];
264                     if (record_named_pet && m_ref.is_named_pet()) {
265                         const auto m2_name = monster_desc(player_ptr, m_ptr, MD_INDEF_VISIBLE);
266                         exe_write_diary(player_ptr, DiaryKind::NAMED_PET, RECORD_NAMED_PET_EARTHQUAKE, m2_name);
267                     }
268                 }
269
270                 delete_monster(player_ptr, yy, xx);
271                 sn = 0;
272             }
273
274             if (sn == 0) {
275                 continue;
276             }
277
278             IDX m_idx_aux = floor_ptr->grid_array[yy][xx].m_idx;
279             floor_ptr->grid_array[yy][xx].m_idx = 0;
280             floor_ptr->grid_array[sy][sx].m_idx = m_idx_aux;
281             m_ptr->fy = sy;
282             m_ptr->fx = sx;
283             update_monster(player_ptr, m_idx_aux, true);
284             lite_spot(player_ptr, yy, xx);
285             lite_spot(player_ptr, sy, sx);
286         }
287     }
288
289     clear_mon_lite(floor_ptr);
290     for (POSITION dy = -r; dy <= r; dy++) {
291         for (POSITION dx = -r; dx <= r; dx++) {
292             POSITION yy = cy + dy;
293             POSITION xx = cx + dx;
294             if (!map[16 + yy - cy][16 + xx - cx]) {
295                 continue;
296             }
297
298             if (!cave_valid_bold(floor_ptr, yy, xx)) {
299                 continue;
300             }
301
302             delete_all_items_from_floor(player_ptr, yy, xx);
303             int t = cave_has_flag_bold(floor_ptr, yy, xx, TerrainCharacteristics::PROJECT) ? randint0(100) : 200;
304             if (t < 20) {
305                 cave_set_feat(player_ptr, yy, xx, feat_granite);
306                 continue;
307             }
308
309             if (t < 70) {
310                 cave_set_feat(player_ptr, yy, xx, feat_quartz_vein);
311                 continue;
312             }
313
314             if (t < 100) {
315                 cave_set_feat(player_ptr, yy, xx, feat_magma_vein);
316                 continue;
317             }
318
319             cave_set_feat(player_ptr, yy, xx, rand_choice(feat_ground_type));
320         }
321     }
322
323     for (POSITION dy = -r; dy <= r; dy++) {
324         for (POSITION dx = -r; dx <= r; dx++) {
325             POSITION yy = cy + dy;
326             POSITION xx = cx + dx;
327             if (!in_bounds(floor_ptr, yy, xx)) {
328                 continue;
329             }
330
331             if (distance(cy, cx, yy, xx) > r) {
332                 continue;
333             }
334
335             auto *g_ptr = &floor_ptr->grid_array[yy][xx];
336             if (g_ptr->is_mirror()) {
337                 g_ptr->info |= CAVE_GLOW;
338                 continue;
339             }
340
341             if (floor_ptr->get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
342                 continue;
343             }
344
345             grid_type *cc_ptr;
346             for (DIRECTION ii = 0; ii < 9; ii++) {
347                 POSITION yyy = yy + ddy_ddd[ii];
348                 POSITION xxx = xx + ddx_ddd[ii];
349                 if (!in_bounds2(floor_ptr, yyy, xxx)) {
350                     continue;
351                 }
352                 cc_ptr = &floor_ptr->grid_array[yyy][xxx];
353                 if (terrains_info[cc_ptr->get_feat_mimic()].flags.has(TerrainCharacteristics::GLOW)) {
354                     g_ptr->info |= CAVE_GLOW;
355                     break;
356                 }
357             }
358         }
359     }
360
361     auto &rfu = RedrawingFlagsUpdater::get_instance();
362     static constexpr auto flags_srf = {
363         StatusRecalculatingFlag::UN_VIEW,
364         StatusRecalculatingFlag::UN_LITE,
365         StatusRecalculatingFlag::VIEW,
366         StatusRecalculatingFlag::LITE,
367         StatusRecalculatingFlag::FLOW,
368         StatusRecalculatingFlag::MONSTER_LITE,
369         StatusRecalculatingFlag::MONSTER_STATUSES,
370     };
371     rfu.set_flags(flags_srf);
372     static constexpr auto flags_mwrf = {
373         MainWindowRedrawingFlag::HEALTH,
374         MainWindowRedrawingFlag::UHEALTH,
375         MainWindowRedrawingFlag::MAP,
376     };
377     rfu.set_flags(flags_mwrf);
378     static constexpr auto flags_swrf = {
379         SubWindowRedrawingFlag::OVERHEAD,
380         SubWindowRedrawingFlag::DUNGEON,
381     };
382     rfu.set_flags(flags_swrf);
383     if (floor_ptr->grid_array[player_ptr->y][player_ptr->x].info & CAVE_GLOW) {
384         set_superstealth(player_ptr, false);
385     }
386
387     return true;
388 }