OSDN Git Service

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