OSDN Git Service

[Refactor] #3650 Grid::get_feat_mimic() から地形特性を得ていた箇所をget_terrain_mimic() に置換した
[hengbandforosx/hengbandosx.git] / src / spell-kind / spells-lite.cpp
1 #include "spell-kind/spells-lite.h"
2 #include "dungeon/dungeon-flag-types.h"
3 #include "effect/attribute-types.h"
4 #include "effect/effect-characteristics.h"
5 #include "effect/effect-processor.h"
6 #include "floor/cave.h"
7 #include "floor/floor-util.h"
8 #include "floor/geometry.h"
9 #include "game-option/map-screen-options.h"
10 #include "grid/grid.h"
11 #include "mind/mind-ninja.h"
12 #include "monster-race/monster-race.h"
13 #include "monster-race/race-flags2.h"
14 #include "monster/monster-describer.h"
15 #include "monster/monster-status-setter.h"
16 #include "monster/monster-status.h"
17 #include "monster/monster-update.h"
18 #include "player/special-defense-types.h"
19 #include "spell-kind/spells-launcher.h"
20 #include "system/angband-system.h"
21 #include "system/dungeon-info.h"
22 #include "system/floor-type-definition.h"
23 #include "system/grid-type-definition.h"
24 #include "system/monster-entity.h"
25 #include "system/monster-race-info.h"
26 #include "system/player-type-definition.h"
27 #include "system/terrain-type-definition.h"
28 #include "target/projection-path-calculator.h"
29 #include "timed-effect/player-blindness.h"
30 #include "timed-effect/timed-effects.h"
31 #include "util/bit-flags-calculator.h"
32 #include "util/point-2d.h"
33 #include "view/display-messages.h"
34 #include "world/world.h"
35 #include <vector>
36
37 using PassBoldFunc = bool (*)(FloorType *, POSITION, POSITION);
38
39 /*!
40  * @brief 指定した座標全てを照らす。
41  * @param player_ptr プレイヤーへの参照ポインタ
42  * @param points 明るくすべき座標たち
43  * @details
44  * <pre>
45  * This routine clears the entire "temp" set.
46  * This routine will Perma-Lite all "temp" grids.
47  * This routine is used (only) by "lite_room()"
48  * Dark grids are illuminated.
49  * Also, process all affected monsters.
50  *
51  * SMART monsters always wake up when illuminated
52  * NORMAL monsters wake up 1/4 the time when illuminated
53  * STUPID monsters wake up 1/10 the time when illuminated
54  * </pre>
55  * @todo この辺、xとyが引数になっているが、player_ptr->xとplayer_ptr->yで全て置き換えが効くはず……
56  */
57 static void cave_temp_room_lite(PlayerType *player_ptr, const std::vector<Pos2D> &points)
58 {
59     for (const auto &point : points) {
60         const POSITION y = point.y;
61         const POSITION x = point.x;
62
63         auto *g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
64         g_ptr->info &= ~(CAVE_TEMP);
65         g_ptr->info |= (CAVE_GLOW);
66         if (g_ptr->m_idx) {
67             PERCENTAGE chance = 25;
68             auto *m_ptr = &player_ptr->current_floor_ptr->m_list[g_ptr->m_idx];
69             auto *r_ptr = &m_ptr->get_monrace();
70             update_monster(player_ptr, g_ptr->m_idx, false);
71             if (r_ptr->behavior_flags.has(MonsterBehaviorType::STUPID)) {
72                 chance = 10;
73             }
74             if (r_ptr->behavior_flags.has(MonsterBehaviorType::SMART)) {
75                 chance = 100;
76             }
77
78             if (m_ptr->is_asleep() && (randint0(100) < chance)) {
79                 (void)set_monster_csleep(player_ptr, g_ptr->m_idx, 0);
80                 if (m_ptr->ml) {
81                     const auto m_name = monster_desc(player_ptr, m_ptr, 0);
82                     msg_format(_("%s^が目を覚ました。", "%s^ wakes up."), m_name.data());
83                 }
84             }
85         }
86
87         note_spot(player_ptr, y, x);
88         lite_spot(player_ptr, y, x);
89         update_local_illumination(player_ptr, y, x);
90     }
91 }
92
93 /*!
94  * @brief 指定した座標全てを暗くする。
95  * @param player_ptr プレイヤーへの参照ポインタ
96  * @param points 暗くすべき座標群
97  */
98 static void cave_temp_room_unlite(PlayerType *player_ptr, const std::vector<Pos2D> &points)
99 {
100     auto &floor = *player_ptr->current_floor_ptr;
101     for (const auto &point : points) {
102         auto &grid = floor.get_grid(point);
103         auto do_dark = !grid.is_mirror();
104         grid.info &= ~(CAVE_TEMP);
105         if (!do_dark) {
106             continue;
107         }
108
109         if (floor.dun_level || !is_daytime()) {
110             for (int j = 0; j < 9; j++) {
111                 const Pos2D pos_neighbor(point.y + ddy_ddd[j], point.x + ddx_ddd[j]);
112                 if (!in_bounds2(&floor, pos_neighbor.y, pos_neighbor.x)) {
113                     continue;
114                 }
115
116                 const auto &grid_neighbor = floor.get_grid(pos_neighbor);
117                 if (grid_neighbor.get_terrain_mimic().flags.has(TerrainCharacteristics::GLOW)) {
118                     do_dark = false;
119                     break;
120                 }
121             }
122
123             if (!do_dark) {
124                 continue;
125             }
126         }
127
128         grid.info &= ~(CAVE_GLOW);
129         if (grid.get_terrain_mimic().flags.has_not(TerrainCharacteristics::REMEMBER)) {
130             if (!view_torch_grids) {
131                 grid.info &= ~(CAVE_MARK);
132             }
133             note_spot(player_ptr, point.y, point.x);
134         }
135
136         if (grid.m_idx) {
137             update_monster(player_ptr, grid.m_idx, false);
138         }
139
140         lite_spot(player_ptr, point.y, point.x);
141         update_local_illumination(player_ptr, point.y, point.x);
142     }
143 }
144
145 /*!
146  * @brief 周辺に関数ポインタの条件に該当する地形がいくつあるかを計算する / Determine how much contiguous open space this grid is next to
147  * @param floor_ptr 配置するフロアの参照ポインタ
148  * @param cy Y座標
149  * @param cx X座標
150  * @param pass_bold 地形条件を返す関数ポインタ
151  * @return 該当地形の数
152  */
153 static int next_to_open(FloorType *floor_ptr, const POSITION cy, const POSITION cx, const PassBoldFunc pass_bold)
154 {
155     int len = 0;
156     int blen = 0;
157     for (int i = 0; i < 16; i++) {
158         POSITION y = cy + ddy_cdd[i % 8];
159         POSITION x = cx + ddx_cdd[i % 8];
160         if (!pass_bold(floor_ptr, y, x)) {
161             if (len > blen) {
162                 blen = len;
163             }
164
165             len = 0;
166         } else {
167             len++;
168         }
169     }
170
171     return std::max(len, blen);
172 }
173
174 /*!
175  * @brief 周辺に関数ポインタの条件に該当する地形がいくつあるかを計算する / Determine how much contiguous open space this grid is next to
176  * @param floor_ptr 配置するフロアの参照ポインタ
177  * @param cy Y座標
178  * @param cx X座標
179  * @param pass_bold 地形条件を返す関数ポインタ
180  * @return 該当地形の数
181  */
182 static int next_to_walls_adj(FloorType *floor_ptr, const POSITION cy, const POSITION cx, const PassBoldFunc pass_bold)
183 {
184     POSITION y, x;
185     int c = 0;
186     for (DIRECTION i = 0; i < 8; i++) {
187         y = cy + ddy_ddd[i];
188         x = cx + ddx_ddd[i];
189
190         if (!pass_bold(floor_ptr, y, x)) {
191             c++;
192         }
193     }
194
195     return c;
196 }
197
198 /*!
199  * @brief (y,x) が指定条件を満たすなら points に加える
200  * @param player_ptr プレイヤーへの参照ポインタ
201  * @param points 座標記録用配列
202  * @param y 部屋内のy座標1点
203  * @param x 部屋内のx座標1点
204  * @param only_room 部屋内地形のみをチェック対象にするならば TRUE
205  * @param pass_bold 地形条件を返す関数ポインタ
206  */
207 static void cave_temp_room_aux(
208     PlayerType *player_ptr, std::vector<Pos2D> &points, const POSITION y, const POSITION x, const bool only_room, const PassBoldFunc pass_bold)
209 {
210     auto *floor_ptr = player_ptr->current_floor_ptr;
211     auto *g_ptr = &floor_ptr->grid_array[y][x];
212
213     // 既に points に追加済みなら何もしない。
214     if (g_ptr->info & (CAVE_TEMP)) {
215         return;
216     }
217
218     if (!(g_ptr->info & (CAVE_ROOM))) {
219         if (only_room) {
220             return;
221         }
222         if (!in_bounds2(floor_ptr, y, x)) {
223             return;
224         }
225         if (distance(player_ptr->y, player_ptr->x, y, x) > AngbandSystem::get_instance().get_max_range()) {
226             return;
227         }
228
229         /* Verify this grid */
230         /*
231          * The reason why it is ==6 instead of >5 is that 8 is impossible
232          * due to the check for cave_bold above.
233          * 7 lights dead-end corridors (you need to do this for the
234          * checkboard interesting rooms, so that the boundary is lit
235          * properly.
236          * This leaves only a check for 6 bounding walls!
237          */
238         if (in_bounds(floor_ptr, y, x) && pass_bold(floor_ptr, y, x) && (next_to_walls_adj(floor_ptr, y, x, pass_bold) == 6) && (next_to_open(floor_ptr, y, x, pass_bold) <= 1)) {
239             return;
240         }
241     }
242
243     // (y,x) を points に追加し、追加済みフラグを立てる。
244     points.emplace_back(y, x);
245     g_ptr->info |= (CAVE_TEMP);
246 }
247
248 /*!
249  * @brief (y,x) が明るくすべきマスなら points に加える
250  * @param player_ptr プレイヤーへの参照ポインタ
251  * @param points 座標記録用配列
252  * @param y 指定Y座標
253  * @param x 指定X座標
254  */
255 static void cave_temp_lite_room_aux(PlayerType *player_ptr, std::vector<Pos2D> &points, const POSITION y, const POSITION x)
256 {
257     cave_temp_room_aux(player_ptr, points, y, x, false, cave_los_bold);
258 }
259
260 /*!
261  * @brief 指定のマスが光を通さず射線のみを通すかを返す。 / Aux function -- see below
262  * @param floor_ptr 配置するフロアの参照ポインタ
263  * @param y 指定Y座標
264  * @param x 指定X座標
265  * @return 射線を通すならばtrueを返す。
266  */
267 static bool cave_pass_dark_bold(FloorType *floor_ptr, POSITION y, POSITION x)
268 {
269     return cave_has_flag_bold(floor_ptr, y, x, TerrainCharacteristics::PROJECT);
270 }
271
272 /*!
273  * @brief (y,x) が暗くすべきマスなら points に加える
274  * @param player_ptr プレイヤーへの参照ポインタ
275  * @param y 指定Y座標
276  * @param x 指定X座標
277  */
278 static void cave_temp_unlite_room_aux(PlayerType *player_ptr, std::vector<Pos2D> &points, const POSITION y, const POSITION x)
279 {
280     cave_temp_room_aux(player_ptr, points, y, x, true, cave_pass_dark_bold);
281 }
282
283 /*!
284  * @brief (y1,x1) を含む全ての部屋を照らす。 / Illuminate any room containing the given location.
285  * @param player_ptr プレイヤーへの参照ポインタ
286  * @param y1 指定Y座標
287  * @param x1 指定X座標
288  *
289  * NOTE: 部屋に限らないかも?
290  */
291 void lite_room(PlayerType *player_ptr, const POSITION y1, const POSITION x1)
292 {
293     // 明るくするマスを記録する配列。
294     std::vector<Pos2D> points;
295
296     auto *floor_ptr = player_ptr->current_floor_ptr;
297
298     // (y1,x1) を起点として明るくするマスを記録していく。
299     // 実質幅優先探索。
300     cave_temp_lite_room_aux(player_ptr, points, y1, x1);
301     for (size_t i = 0; i < size(points); i++) {
302         const auto &point = points[i];
303         const POSITION y = point.y;
304         const POSITION x = point.x;
305
306         if (!cave_los_bold(floor_ptr, y, x)) {
307             continue;
308         }
309
310         cave_temp_lite_room_aux(player_ptr, points, y + 1, x);
311         cave_temp_lite_room_aux(player_ptr, points, y - 1, x);
312         cave_temp_lite_room_aux(player_ptr, points, y, x + 1);
313         cave_temp_lite_room_aux(player_ptr, points, y, x - 1);
314
315         cave_temp_lite_room_aux(player_ptr, points, y + 1, x + 1);
316         cave_temp_lite_room_aux(player_ptr, points, y - 1, x - 1);
317         cave_temp_lite_room_aux(player_ptr, points, y - 1, x + 1);
318         cave_temp_lite_room_aux(player_ptr, points, y + 1, x - 1);
319     }
320
321     // 記録したマスを実際に明るくする。
322     cave_temp_room_lite(player_ptr, points);
323
324     // 超隠密状態の更新。
325     if (floor_ptr->grid_array[player_ptr->y][player_ptr->x].info & CAVE_GLOW) {
326         set_superstealth(player_ptr, false);
327     }
328 }
329
330 /*!
331  * @brief (y1,x1) を含む全ての部屋を暗くする。 / Darken all rooms containing the given location
332  * @param player_ptr プレイヤーへの参照ポインタ
333  * @param y1 指定Y座標
334  * @param x1 指定X座標
335  */
336 void unlite_room(PlayerType *player_ptr, const POSITION y1, const POSITION x1)
337 {
338     // 暗くするマスを記録する配列。
339     std::vector<Pos2D> points;
340
341     auto *floor_ptr = player_ptr->current_floor_ptr;
342
343     // (y1,x1) を起点として暗くするマスを記録していく。
344     // 実質幅優先探索。
345     cave_temp_unlite_room_aux(player_ptr, points, y1, x1);
346     for (size_t i = 0; i < size(points); i++) {
347         const auto &point = points[i];
348         const POSITION y = point.y;
349         const POSITION x = point.x;
350
351         if (!cave_pass_dark_bold(floor_ptr, y, x)) {
352             continue;
353         }
354
355         cave_temp_unlite_room_aux(player_ptr, points, y + 1, x);
356         cave_temp_unlite_room_aux(player_ptr, points, y - 1, x);
357         cave_temp_unlite_room_aux(player_ptr, points, y, x + 1);
358         cave_temp_unlite_room_aux(player_ptr, points, y, x - 1);
359
360         cave_temp_unlite_room_aux(player_ptr, points, y + 1, x + 1);
361         cave_temp_unlite_room_aux(player_ptr, points, y - 1, x - 1);
362         cave_temp_unlite_room_aux(player_ptr, points, y - 1, x + 1);
363         cave_temp_unlite_room_aux(player_ptr, points, y + 1, x - 1);
364     }
365
366     // 記録したマスを実際に暗くする。
367     cave_temp_room_unlite(player_ptr, points);
368 }
369
370 /*!
371  * @brief スターライトの効果を発生させる
372  * @param player_ptr プレイヤーへの参照ポインタ
373  * @param magic 魔法による効果であればTRUE、スターライトの杖による効果であればFALSE
374  * @return 常にTRUE
375  */
376 bool starlight(PlayerType *player_ptr, bool magic)
377 {
378     if (!player_ptr->effects()->blindness()->is_blind() && !magic) {
379         msg_print(_("杖の先が明るく輝いた...", "The end of the staff glows brightly..."));
380     }
381
382     int num = damroll(5, 3);
383     auto y = 0;
384     auto x = 0;
385     for (int k = 0; k < num; k++) {
386         auto attempts = 1000;
387         while (attempts--) {
388             scatter(player_ptr, &y, &x, player_ptr->y, player_ptr->x, 4, PROJECT_LOS);
389             const Pos2D pos(y, x);
390             if (!cave_has_flag_bold(player_ptr->current_floor_ptr, pos.y, pos.x, TerrainCharacteristics::PROJECT)) {
391                 continue;
392             }
393
394             if (!player_ptr->is_located_at(pos)) {
395                 break;
396             }
397         }
398
399         constexpr uint flags = PROJECT_BEAM | PROJECT_THRU | PROJECT_GRID | PROJECT_KILL | PROJECT_LOS;
400         project(player_ptr, 0, 0, y, x, damroll(6 + player_ptr->lev / 8, 10), AttributeType::LITE_WEAK, flags);
401     }
402
403     return true;
404 }
405
406 /*!
407  * @brief プレイヤー位置を中心にLITE_WEAK属性を通じた照明処理を行う / Hack -- call light around the player Affect all monsters in the projection radius
408  * @param player_ptr プレイヤーへの参照ポインタ
409  * @param dam 威力
410  * @param rad 効果半径
411  * @return 作用が実際にあった場合TRUEを返す
412  */
413 bool lite_area(PlayerType *player_ptr, int dam, POSITION rad)
414 {
415     if (player_ptr->current_floor_ptr->get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS)) {
416         msg_print(_("ダンジョンが光を吸収した。", "The darkness of this dungeon absorbs your light."));
417         return false;
418     }
419
420     if (!player_ptr->effects()->blindness()->is_blind()) {
421         msg_print(_("白い光が辺りを覆った。", "You are surrounded by a white light."));
422     }
423
424     BIT_FLAGS flg = PROJECT_GRID | PROJECT_KILL;
425     (void)project(player_ptr, 0, rad, player_ptr->y, player_ptr->x, dam, AttributeType::LITE_WEAK, flg);
426
427     lite_room(player_ptr, player_ptr->y, player_ptr->x);
428
429     return true;
430 }
431
432 /*!
433  * @brief プレイヤー位置を中心にLITE_DARK属性を通じた消灯処理を行う / Hack -- call light around the player Affect all monsters in the projection radius
434  * @param player_ptr プレイヤーへの参照ポインタ
435  * @param dam 威力
436  * @param rad 効果半径
437  * @return 作用が実際にあった場合TRUEを返す
438  */
439 bool unlite_area(PlayerType *player_ptr, int dam, POSITION rad)
440 {
441     if (!player_ptr->effects()->blindness()->is_blind()) {
442         msg_print(_("暗闇が辺りを覆った。", "Darkness surrounds you."));
443     }
444
445     BIT_FLAGS flg = PROJECT_GRID | PROJECT_KILL;
446     (void)project(player_ptr, 0, rad, player_ptr->y, player_ptr->x, dam, AttributeType::DARK_WEAK, flg);
447
448     unlite_room(player_ptr, player_ptr->y, player_ptr->x);
449
450     return true;
451 }
452
453 /*!
454  * @brief LITE_WEAK属性による光源ビーム処理
455  * @param player_ptr プレイヤーへの参照ポインタ
456  * @param dir 方向(5ならばグローバル変数 target_col/target_row の座標を目標にする)
457  * @param dam 威力
458  * @return 作用が実際にあった場合TRUEを返す
459  */
460 bool lite_line(PlayerType *player_ptr, DIRECTION dir, int dam)
461 {
462     BIT_FLAGS flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_KILL;
463     return project_hook(player_ptr, AttributeType::LITE_WEAK, dir, dam, flg);
464 }