OSDN Git Service

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