OSDN Git Service

Merge pull request #3651 from Hourier/Make-TerrainList
[hengbandforosx/hengbandosx.git] / src / spell-kind / spells-floor.cpp
1 /*!
2  * @brief フロアに影響のある魔法の処理
3  * @date 2019/02/21
4  * @author deskull
5  */
6
7 #include "spell-kind/spells-floor.h"
8 #include "action/travel-execution.h"
9 #include "cmd-io/cmd-dump.h"
10 #include "core/window-redrawer.h"
11 #include "dungeon/dungeon-flag-types.h"
12 #include "dungeon/quest.h"
13 #include "effect/attribute-types.h"
14 #include "flavor/flavor-describer.h"
15 #include "flavor/object-flavor-types.h"
16 #include "floor/cave.h"
17 #include "floor/floor-object.h"
18 #include "floor/floor-save.h"
19 #include "floor/floor-util.h"
20 #include "floor/geometry.h"
21 #include "game-option/birth-options.h"
22 #include "game-option/cheat-options.h"
23 #include "game-option/map-screen-options.h"
24 #include "game-option/play-record-options.h"
25 #include "grid/feature-flag-types.h"
26 #include "grid/feature.h"
27 #include "grid/grid.h"
28 #include "io/write-diary.h"
29 #include "mind/mind-ninja.h"
30 #include "monster-floor/monster-lite.h"
31 #include "monster-race/monster-race.h"
32 #include "monster-race/race-flags1.h"
33 #include "monster/monster-describer.h"
34 #include "monster/monster-description-types.h"
35 #include "monster/monster-info.h"
36 #include "monster/monster-status.h"
37 #include "monster/smart-learn-types.h"
38 #include "object-enchant/special-object-flags.h"
39 #include "object/object-mark-types.h"
40 #include "perception/object-perception.h"
41 #include "player/player-status-flags.h"
42 #include "player/special-defense-types.h"
43 #include "spell-kind/spells-teleport.h"
44 #include "status/bad-status-setter.h"
45 #include "system/artifact-type-definition.h"
46 #include "system/dungeon-info.h"
47 #include "system/floor-type-definition.h"
48 #include "system/grid-type-definition.h"
49 #include "system/item-entity.h"
50 #include "system/monster-entity.h"
51 #include "system/monster-race-info.h"
52 #include "system/player-type-definition.h"
53 #include "system/redrawing-flags-updater.h"
54 #include "system/terrain-type-definition.h"
55 #include "util/bit-flags-calculator.h"
56 #include "view/display-messages.h"
57
58 /*
59  * @brief 啓蒙/陽光召喚処理
60  * @param player_ptr プレイヤーへの参照ポインタ
61  * @param ninja 忍者かどうか
62  */
63 void wiz_lite(PlayerType *player_ptr, bool ninja)
64 {
65     /* Memorize objects */
66     auto &floor = *player_ptr->current_floor_ptr;
67     for (OBJECT_IDX i = 1; i < floor.o_max; i++) {
68         auto *o_ptr = &floor.o_list[i];
69         if (!o_ptr->is_valid()) {
70             continue;
71         }
72         if (o_ptr->is_held_by_monster()) {
73             continue;
74         }
75         o_ptr->marked.set(OmType::FOUND);
76     }
77
78     /* Scan all normal grids */
79     const auto &terrains = TerrainList::get_instance();
80     for (POSITION y = 1; y < floor.height - 1; y++) {
81         /* Scan all normal grids */
82         for (POSITION x = 1; x < floor.width - 1; x++) {
83             auto *g_ptr = &floor.grid_array[y][x];
84
85             /* Memorize terrain of the grid */
86             g_ptr->info |= (CAVE_KNOWN);
87
88             /* Feature code (applying "mimic" field) */
89             FEAT_IDX feat = g_ptr->get_feat_mimic();
90             auto *t_ptr = &terrains[feat];
91
92             /* Scan all neighbors */
93             for (OBJECT_IDX i = 0; i < 9; i++) {
94                 POSITION yy = y + ddy_ddd[i];
95                 POSITION xx = x + ddx_ddd[i];
96                 g_ptr = &floor.grid_array[yy][xx];
97
98                 /* Feature code (applying "mimic" field) */
99                 t_ptr = &terrains[g_ptr->get_feat_mimic()];
100
101                 /* Perma-lite the grid */
102                 if (floor.get_dungeon_definition().flags.has_not(DungeonFeatureType::DARKNESS) && !ninja) {
103                     g_ptr->info |= (CAVE_GLOW);
104                 }
105
106                 /* Memorize normal features */
107                 if (t_ptr->flags.has(TerrainCharacteristics::REMEMBER)) {
108                     /* Memorize the grid */
109                     g_ptr->info |= (CAVE_MARK);
110                 }
111
112                 /* Perma-lit grids (newly and previously) */
113                 else if (g_ptr->info & CAVE_GLOW) {
114                     /* Normally, memorize floors (see above) */
115                     if (view_perma_grids && !view_torch_grids) {
116                         /* Memorize the grid */
117                         g_ptr->info |= (CAVE_MARK);
118                     }
119                 }
120             }
121         }
122     }
123
124     auto &rfu = RedrawingFlagsUpdater::get_instance();
125     rfu.set_flag(StatusRecalculatingFlag::MONSTER_STATUSES);
126     rfu.set_flag(MainWindowRedrawingFlag::MAP);
127     static constexpr auto flags_swrf = {
128         SubWindowRedrawingFlag::OVERHEAD,
129         SubWindowRedrawingFlag::DUNGEON,
130         SubWindowRedrawingFlag::FOUND_ITEMS,
131     };
132     rfu.set_flags(flags_swrf);
133     if (floor.grid_array[player_ptr->y][player_ptr->x].info & CAVE_GLOW) {
134         set_superstealth(player_ptr, false);
135     }
136 }
137
138 /*
139  * Forget the dungeon map (ala "Thinking of Maud...").
140  */
141 void wiz_dark(PlayerType *player_ptr)
142 {
143     /* Forget every grid */
144     for (POSITION y = 1; y < player_ptr->current_floor_ptr->height - 1; y++) {
145         for (POSITION x = 1; x < player_ptr->current_floor_ptr->width - 1; x++) {
146             auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
147
148             /* Process the grid */
149             g_ptr->info &= ~(CAVE_MARK | CAVE_IN_DETECT | CAVE_KNOWN);
150             g_ptr->info |= (CAVE_UNSAFE);
151         }
152     }
153
154     /* Forget every grid on horizontal edge */
155     for (POSITION x = 0; x < player_ptr->current_floor_ptr->width; x++) {
156         player_ptr->current_floor_ptr->grid_array[0][x].info &= ~(CAVE_MARK);
157         player_ptr->current_floor_ptr->grid_array[player_ptr->current_floor_ptr->height - 1][x].info &= ~(CAVE_MARK);
158     }
159
160     /* Forget every grid on vertical edge */
161     for (POSITION y = 1; y < (player_ptr->current_floor_ptr->height - 1); y++) {
162         player_ptr->current_floor_ptr->grid_array[y][0].info &= ~(CAVE_MARK);
163         player_ptr->current_floor_ptr->grid_array[y][player_ptr->current_floor_ptr->width - 1].info &= ~(CAVE_MARK);
164     }
165
166     /* Forget all objects */
167     for (OBJECT_IDX i = 1; i < player_ptr->current_floor_ptr->o_max; i++) {
168         auto *o_ptr = &player_ptr->current_floor_ptr->o_list[i];
169
170         if (!o_ptr->is_valid()) {
171             continue;
172         }
173         if (o_ptr->is_held_by_monster()) {
174             continue;
175         }
176
177         /* Forget the object */
178         // 意図としては OmType::TOUCHED を維持しつつ OmType::FOUND を消す事と思われるが一応元のロジックを維持しておく
179         o_ptr->marked &= { OmType::TOUCHED };
180     }
181
182     /* Forget travel route when we have forgotten map */
183     forget_travel_flow(player_ptr->current_floor_ptr);
184
185     auto &rfu = RedrawingFlagsUpdater::get_instance();
186     static constexpr auto flags_srf = {
187         StatusRecalculatingFlag::UN_VIEW,
188         StatusRecalculatingFlag::UN_LITE,
189         StatusRecalculatingFlag::VIEW,
190         StatusRecalculatingFlag::LITE,
191         StatusRecalculatingFlag::MONSTER_LITE,
192         StatusRecalculatingFlag::MONSTER_STATUSES,
193     };
194     rfu.set_flags(flags_srf);
195     rfu.set_flag(MainWindowRedrawingFlag::MAP);
196     static constexpr auto flags_swrf = {
197         SubWindowRedrawingFlag::OVERHEAD,
198         SubWindowRedrawingFlag::DUNGEON,
199         SubWindowRedrawingFlag::FOUND_ITEMS,
200     };
201     rfu.set_flags(flags_swrf);
202 }
203
204 /*
205  * Hack -- map the current panel (plus some) ala "magic mapping"
206  */
207 void map_area(PlayerType *player_ptr, POSITION range)
208 {
209     auto &floor = *player_ptr->current_floor_ptr;
210     if (floor.get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
211         range /= 3;
212     }
213
214     /* Scan that area */
215     const auto &terrains = TerrainList::get_instance();
216     for (POSITION y = 1; y < floor.height - 1; y++) {
217         for (POSITION x = 1; x < floor.width - 1; x++) {
218             if (distance(player_ptr->y, player_ptr->x, y, x) > range) {
219                 continue;
220             }
221
222             grid_type *g_ptr;
223             g_ptr = &floor.grid_array[y][x];
224
225             /* Memorize terrain of the grid */
226             g_ptr->info |= (CAVE_KNOWN);
227
228             /* Feature code (applying "mimic" field) */
229             FEAT_IDX feat = g_ptr->get_feat_mimic();
230             auto *t_ptr = &terrains[feat];
231
232             /* Memorize normal features */
233             if (t_ptr->flags.has(TerrainCharacteristics::REMEMBER)) {
234                 /* Memorize the object */
235                 g_ptr->info |= (CAVE_MARK);
236             }
237
238             /* Memorize known walls */
239             for (int i = 0; i < 8; i++) {
240                 g_ptr = &floor.grid_array[y + ddy_ddd[i]][x + ddx_ddd[i]];
241
242                 /* Feature code (applying "mimic" field) */
243                 feat = g_ptr->get_feat_mimic();
244                 t_ptr = &terrains[feat];
245
246                 /* Memorize walls (etc) */
247                 if (t_ptr->flags.has(TerrainCharacteristics::REMEMBER)) {
248                     /* Memorize the walls */
249                     g_ptr->info |= (CAVE_MARK);
250                 }
251             }
252         }
253     }
254
255     auto &rfu = RedrawingFlagsUpdater::get_instance();
256     rfu.set_flag(MainWindowRedrawingFlag::MAP);
257     static constexpr auto flags_swrf = {
258         SubWindowRedrawingFlag::OVERHEAD,
259         SubWindowRedrawingFlag::DUNGEON,
260     };
261     rfu.set_flags(flags_swrf);
262 }
263
264 /*!
265  * @brief *破壊*処理を行う / The spell of destruction
266  * @param y1 破壊の中心Y座標
267  * @param x1 破壊の中心X座標
268  * @param r 破壊の半径
269  * @param in_generate ダンジョンフロア生成中の処理ならばTRUE
270  * @return 効力があった場合TRUEを返す
271  * @details
272  * <pre>
273  * This spell "deletes" monsters (instead of "killing" them).
274  *
275  * Later we may use one function for both "destruction" and
276  * "earthquake" by using the "full" to select "destruction".
277  * </pre>
278  */
279 bool destroy_area(PlayerType *player_ptr, POSITION y1, POSITION x1, POSITION r, bool in_generate)
280 {
281     /* Prevent destruction of quest levels and town */
282     auto *floor_ptr = player_ptr->current_floor_ptr;
283     if ((floor_ptr->is_in_quest() && QuestType::is_fixed(floor_ptr->quest_number)) || !floor_ptr->dun_level) {
284         return false;
285     }
286
287     /* Lose monster light */
288     if (!in_generate) {
289         clear_mon_lite(floor_ptr);
290     }
291
292     /* Big area of affect */
293     bool flag = false;
294     for (POSITION y = (y1 - r); y <= (y1 + r); y++) {
295         for (POSITION x = (x1 - r); x <= (x1 + r); x++) {
296             if (!in_bounds(floor_ptr, y, x)) {
297                 continue;
298             }
299
300             /* Extract the distance */
301             int k = distance(y1, x1, y, x);
302
303             /* Stay in the circle of death */
304             if (k > r) {
305                 continue;
306             }
307             grid_type *g_ptr;
308             g_ptr = &floor_ptr->grid_array[y][x];
309
310             /* Lose room and vault */
311             g_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
312
313             /* Lose light and knowledge */
314             g_ptr->info &= ~(CAVE_MARK | CAVE_GLOW | CAVE_KNOWN);
315
316             if (!in_generate) /* Normal */
317             {
318                 /* Lose unsafety */
319                 g_ptr->info &= ~(CAVE_UNSAFE);
320
321                 /* Hack -- Notice player affect */
322                 if (player_bold(player_ptr, y, x)) {
323                     /* Hurt the player later */
324                     flag = true;
325
326                     /* Do not hurt this grid */
327                     continue;
328                 }
329             }
330
331             /* Hack -- Skip the epicenter */
332             if ((y == y1) && (x == x1)) {
333                 continue;
334             }
335
336             if (g_ptr->m_idx) {
337                 auto *m_ptr = &floor_ptr->m_list[g_ptr->m_idx];
338                 auto *r_ptr = &m_ptr->get_monrace();
339
340                 if (in_generate) /* In generation */
341                 {
342                     /* Delete the monster (if any) */
343                     delete_monster(player_ptr, y, x);
344                 } else if (r_ptr->flags1 & RF1_QUESTOR) {
345                     /* Heal the monster */
346                     m_ptr->hp = m_ptr->maxhp;
347
348                     /* Try to teleport away quest monsters */
349                     if (!teleport_away(player_ptr, g_ptr->m_idx, (r * 2) + 1, TELEPORT_DEC_VALOUR)) {
350                         continue;
351                     }
352                 } else {
353                     if (record_named_pet && m_ptr->is_named_pet()) {
354                         const auto m_name = monster_desc(player_ptr, m_ptr, MD_INDEF_VISIBLE);
355                         exe_write_diary(player_ptr, DiaryKind::NAMED_PET, RECORD_NAMED_PET_DESTROY, m_name);
356                     }
357
358                     /* Delete the monster (if any) */
359                     delete_monster(player_ptr, y, x);
360                 }
361             }
362
363             /* During generation, destroyed artifacts are "preserved" */
364             if (preserve_mode || in_generate) {
365                 /* Scan all objects in the grid */
366                 for (const auto this_o_idx : g_ptr->o_idx_list) {
367                     ItemEntity *o_ptr;
368                     o_ptr = &floor_ptr->o_list[this_o_idx];
369
370                     /* Hack -- Preserve unknown artifacts */
371                     if (o_ptr->is_fixed_artifact() && (!o_ptr->is_known() || in_generate)) {
372                         o_ptr->get_fixed_artifact().is_generated = false;
373
374                         if (in_generate && cheat_peek) {
375                             const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_NAME_ONLY | OD_STORE));
376                             msg_format(_("伝説のアイテム (%s) は生成中に*破壊*された。", "Artifact (%s) was *destroyed* during generation."), item_name.data());
377                         }
378                     } else if (in_generate && cheat_peek && o_ptr->is_random_artifact()) {
379                         msg_print(
380                             _("ランダム・アーティファクトの1つは生成中に*破壊*された。", "One of the random artifacts was *destroyed* during generation."));
381                     }
382                 }
383             }
384
385             delete_all_items_from_floor(player_ptr, y, x);
386
387             /* Destroy "non-permanent" grids */
388             if (g_ptr->cave_has_flag(TerrainCharacteristics::PERMANENT)) {
389                 continue;
390             }
391
392             /* Wall (or floor) type */
393             int t = randint0(200);
394
395             if (!in_generate) /* Normal */
396             {
397                 if (t < 20) {
398                     /* Create granite wall */
399                     cave_set_feat(player_ptr, y, x, feat_granite);
400                 } else if (t < 70) {
401                     /* Create quartz vein */
402                     cave_set_feat(player_ptr, y, x, feat_quartz_vein);
403                 } else if (t < 100) {
404                     /* Create magma vein */
405                     cave_set_feat(player_ptr, y, x, feat_magma_vein);
406                 } else {
407                     /* Create floor */
408                     cave_set_feat(player_ptr, y, x, rand_choice(feat_ground_type));
409                 }
410
411                 continue;
412             }
413
414             if (t < 20) {
415                 /* Create granite wall */
416                 place_grid(player_ptr, g_ptr, GB_EXTRA);
417             } else if (t < 70) {
418                 /* Create quartz vein */
419                 g_ptr->feat = feat_quartz_vein;
420             } else if (t < 100) {
421                 /* Create magma vein */
422                 g_ptr->feat = feat_magma_vein;
423             } else {
424                 /* Create floor */
425                 place_grid(player_ptr, g_ptr, GB_FLOOR);
426             }
427
428             /* Clear garbage of hidden trap or door */
429             g_ptr->mimic = 0;
430         }
431     }
432
433     if (in_generate) {
434         return true;
435     }
436
437     /* Process "re-glowing" */
438     for (POSITION y = (y1 - r); y <= (y1 + r); y++) {
439         for (POSITION x = (x1 - r); x <= (x1 + r); x++) {
440             if (!in_bounds(floor_ptr, y, x)) {
441                 continue;
442             }
443
444             /* Extract the distance */
445             int k = distance(y1, x1, y, x);
446
447             /* Stay in the circle of death */
448             if (k > r) {
449                 continue;
450             }
451             grid_type *g_ptr;
452             g_ptr = &floor_ptr->grid_array[y][x];
453
454             if (g_ptr->is_mirror()) {
455                 g_ptr->info |= CAVE_GLOW;
456                 continue;
457             }
458
459             if (floor_ptr->get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
460                 continue;
461             }
462
463             DIRECTION i;
464             POSITION yy, xx;
465             grid_type *cc_ptr;
466
467             for (i = 0; i < 9; i++) {
468                 yy = y + ddy_ddd[i];
469                 xx = x + ddx_ddd[i];
470                 if (!in_bounds2(floor_ptr, yy, xx)) {
471                     continue;
472                 }
473                 cc_ptr = &floor_ptr->grid_array[yy][xx];
474                 if (terrains_info[cc_ptr->get_feat_mimic()].flags.has(TerrainCharacteristics::GLOW)) {
475                     g_ptr->info |= CAVE_GLOW;
476                     break;
477                 }
478             }
479         }
480     }
481
482     if (flag) {
483         msg_print(_("燃えるような閃光が発生した!", "There is a searing blast of light!"));
484         if (!has_resist_blind(player_ptr) && !has_resist_lite(player_ptr)) {
485             (void)BadStatusSetter(player_ptr).mod_blindness(10 + randint1(10));
486         }
487     }
488
489     forget_flow(floor_ptr);
490     auto &rfu = RedrawingFlagsUpdater::get_instance();
491     static constexpr auto flags_srf = {
492         StatusRecalculatingFlag::UN_VIEW,
493         StatusRecalculatingFlag::UN_LITE,
494         StatusRecalculatingFlag::VIEW,
495         StatusRecalculatingFlag::LITE,
496         StatusRecalculatingFlag::FLOW,
497         StatusRecalculatingFlag::MONSTER_LITE,
498         StatusRecalculatingFlag::MONSTER_STATUSES,
499     };
500     rfu.set_flags(flags_srf);
501     rfu.set_flag(MainWindowRedrawingFlag::MAP);
502     static constexpr auto flags_swrf = {
503         SubWindowRedrawingFlag::OVERHEAD,
504         SubWindowRedrawingFlag::DUNGEON,
505     };
506     rfu.set_flags(flags_swrf);
507     if (floor_ptr->grid_array[player_ptr->y][player_ptr->x].info & CAVE_GLOW) {
508         set_superstealth(player_ptr, false);
509     }
510
511     return true;
512 }