OSDN Git Service

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