OSDN Git Service

Merge pull request #1504 from Hourier/feature/Rename-Player-Pointer
[hengbandforosx/hengbandosx.git] / src / spell-kind / spells-lite.cpp
1 #include "dungeon/dungeon-flag-types.h"
2 #include "dungeon/dungeon.h"
3 #include "effect/effect-characteristics.h"
4 #include "effect/effect-processor.h"
5 #include "floor/cave.h"
6 #include "floor/geometry.h"
7 #include "floor/floor-util.h"
8 #include "game-option/map-screen-options.h"
9 #include "grid/feature.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 "spell-kind/spells-lite.h"
21 #include "spell/spell-types.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(player_type *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         grid_type *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             monster_type *m_ptr = &player_ptr->current_floor_ptr->m_list[g_ptr->m_idx];
66             monster_race *r_ptr = &r_info[m_ptr->r_idx];
67             update_monster(player_ptr, g_ptr->m_idx, false);
68             if (r_ptr->flags2 & (RF2_STUPID))
69                 chance = 10;
70             if (r_ptr->flags2 & (RF2_SMART))
71                 chance = 100;
72
73             if (monster_csleep_remaining(m_ptr) && (randint0(100) < chance)) {
74                 (void)set_monster_csleep(player_ptr, g_ptr->m_idx, 0);
75                 if (m_ptr->ml) {
76                     GAME_TEXT m_name[MAX_NLEN];
77                     monster_desc(player_ptr, m_name, m_ptr, 0);
78                     msg_format(_("%^sが目を覚ました。", "%^s wakes up."), m_name);
79                 }
80             }
81         }
82
83         note_spot(player_ptr, y, x);
84         lite_spot(player_ptr, y, x);
85         update_local_illumination(player_ptr, y, x);
86     }
87 }
88
89 /*!
90  * @brief 指定した座標全てを暗くする。
91  * @param player_ptr プレーヤーへの参照ポインタ
92  * @param points 暗くすべき座標たち
93  * @details
94  * <pre>
95  * This routine clears the entire "temp" set.
96  * This routine will "darken" all "temp" grids.
97  * In addition, some of these grids will be "unmarked".
98  * This routine is used (only) by "unlite_room()"
99  * Also, process all affected monsters
100  * </pre>
101  * @todo この辺、xとyが引数になっているが、player_ptr->xとplayer_ptr->yで全て置き換えが効くはず……
102  */
103 static void cave_temp_room_unlite(player_type *player_ptr, const std::vector<Pos2D> &points)
104 {
105     for (const auto &point : points) {
106         const POSITION y = point.y;
107         const POSITION x = point.x;
108
109         grid_type *g_ptr = &player_ptr->current_floor_ptr->grid_array[y][x];
110         bool do_dark = !g_ptr->is_mirror();
111         g_ptr->info &= ~(CAVE_TEMP);
112         if (!do_dark)
113             continue;
114
115         if (player_ptr->current_floor_ptr->dun_level || !is_daytime()) {
116             for (int j = 0; j < 9; j++) {
117                 POSITION by = y + ddy_ddd[j];
118                 POSITION bx = x + ddx_ddd[j];
119
120                 if (in_bounds2(player_ptr->current_floor_ptr, by, bx)) {
121                     grid_type *cc_ptr = &player_ptr->current_floor_ptr->grid_array[by][bx];
122
123                     if (f_info[cc_ptr->get_feat_mimic()].flags.has(FF::GLOW)) {
124                         do_dark = false;
125                         break;
126                     }
127                 }
128             }
129
130             if (!do_dark)
131                 continue;
132         }
133
134         g_ptr->info &= ~(CAVE_GLOW);
135         if (f_info[g_ptr->get_feat_mimic()].flags.has_not(FF::REMEMBER)) {
136             if (!view_torch_grids)
137                 g_ptr->info &= ~(CAVE_MARK);
138             note_spot(player_ptr, y, x);
139         }
140
141         if (g_ptr->m_idx) {
142             update_monster(player_ptr, g_ptr->m_idx, false);
143         }
144
145         lite_spot(player_ptr, y, x);
146         update_local_illumination(player_ptr, y, x);
147     }
148 }
149
150 /*!
151  * @brief 周辺に関数ポインタの条件に該当する地形がいくつあるかを計算する / Determine how much contiguous open space this grid is next to
152  * @param floor_ptr 配置するフロアの参照ポインタ
153  * @param cy Y座標
154  * @param cx X座標
155  * @param pass_bold 地形条件を返す関数ポインタ
156  * @return 該当地形の数
157  */
158 static int next_to_open(floor_type *floor_ptr, const POSITION cy, const POSITION cx, const PassBoldFunc pass_bold)
159 {
160     int len = 0;
161     int blen = 0;
162     for (int i = 0; i < 16; i++) {
163         POSITION y = cy + ddy_cdd[i % 8];
164         POSITION x = cx + ddx_cdd[i % 8];
165         if (!pass_bold(floor_ptr, y, x)) {
166             if (len > blen) {
167                 blen = len;
168             }
169
170             len = 0;
171         } else {
172             len++;
173         }
174     }
175
176     return MAX(len, blen);
177 }
178
179 /*!
180  * @brief 周辺に関数ポインタの条件に該当する地形がいくつあるかを計算する / Determine how much contiguous open space this grid is next to
181  * @param floor_ptr 配置するフロアの参照ポインタ
182  * @param cy Y座標
183  * @param cx X座標
184  * @param pass_bold 地形条件を返す関数ポインタ
185  * @return 該当地形の数
186  */
187 static int next_to_walls_adj(floor_type *floor_ptr, const POSITION cy, const POSITION cx, const PassBoldFunc pass_bold)
188 {
189     POSITION y, x;
190     int c = 0;
191     for (DIRECTION i = 0; i < 8; i++) {
192         y = cy + ddy_ddd[i];
193         x = cx + ddx_ddd[i];
194
195         if (!pass_bold(floor_ptr, y, x))
196             c++;
197     }
198
199     return c;
200 }
201
202 /*!
203  * @brief (y,x) が指定条件を満たすなら points に加える
204  * @param player_ptr プレーヤーへの参照ポインタ
205  * @param points 座標記録用配列
206  * @param y 部屋内のy座標1点
207  * @param x 部屋内のx座標1点
208  * @param only_room 部屋内地形のみをチェック対象にするならば TRUE
209  * @param pass_bold 地形条件を返す関数ポインタ
210  */
211 static void cave_temp_room_aux(
212     player_type *player_ptr, std::vector<Pos2D> &points, const POSITION y, const POSITION x, const bool only_room, const PassBoldFunc pass_bold)
213 {
214     floor_type *floor_ptr = player_ptr->current_floor_ptr;
215     grid_type *g_ptr = &floor_ptr->grid_array[y][x];
216
217     // 既に points に追加済みなら何もしない。
218     if (g_ptr->info & (CAVE_TEMP))
219         return;
220
221     if (!(g_ptr->info & (CAVE_ROOM))) {
222         if (only_room)
223             return;
224         if (!in_bounds2(floor_ptr, y, x))
225             return;
226         if (distance(player_ptr->y, player_ptr->x, y, x) > get_max_range(player_ptr))
227             return;
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)
239             && (next_to_open(floor_ptr, y, x, pass_bold) <= 1))
240             return;
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(player_type *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(floor_type *floor_ptr, POSITION y, POSITION x) { return cave_has_flag_bold(floor_ptr, y, x, FF::PROJECT); }
268
269 /*!
270  * @brief (y,x) が暗くすべきマスなら points に加える
271  * @param player_ptr プレーヤーへの参照ポインタ
272  * @param y 指定Y座標
273  * @param x 指定X座標
274  */
275 static void cave_temp_unlite_room_aux(player_type *player_ptr, std::vector<Pos2D> &points, const POSITION y, const POSITION x)
276 {
277     cave_temp_room_aux(player_ptr, points, y, x, true, cave_pass_dark_bold);
278 }
279
280 /*!
281  * @brief (y1,x1) を含む全ての部屋を照らす。 / Illuminate any room containing the given location.
282  * @param player_ptr プレーヤーへの参照ポインタ
283  * @param y1 指定Y座標
284  * @param x1 指定X座標
285  *
286  * NOTE: 部屋に限らないかも?
287  */
288 void lite_room(player_type *player_ptr, const POSITION y1, const POSITION x1)
289 {
290     // 明るくするマスを記録する配列。
291     std::vector<Pos2D> points;
292
293     floor_type *floor_ptr = player_ptr->current_floor_ptr;
294
295     // (y1,x1) を起点として明るくするマスを記録していく。
296     // 実質幅優先探索。
297     cave_temp_lite_room_aux(player_ptr, points, y1, x1);
298     for (size_t i = 0; i < size(points); i++) {
299         const auto &point = points[i];
300         const POSITION y = point.y;
301         const POSITION x = point.x;
302
303         if (!cave_los_bold(floor_ptr, y, x))
304             continue;
305
306         cave_temp_lite_room_aux(player_ptr, points, y + 1, x);
307         cave_temp_lite_room_aux(player_ptr, points, y - 1, x);
308         cave_temp_lite_room_aux(player_ptr, points, y, x + 1);
309         cave_temp_lite_room_aux(player_ptr, points, y, x - 1);
310
311         cave_temp_lite_room_aux(player_ptr, points, y + 1, x + 1);
312         cave_temp_lite_room_aux(player_ptr, points, y - 1, x - 1);
313         cave_temp_lite_room_aux(player_ptr, points, y - 1, x + 1);
314         cave_temp_lite_room_aux(player_ptr, points, y + 1, x - 1);
315     }
316
317     // 記録したマスを実際に明るくする。
318     cave_temp_room_lite(player_ptr, points);
319
320     // 超隠密状態の更新。
321     if (player_ptr->special_defense & NINJA_S_STEALTH) {
322         if (floor_ptr->grid_array[player_ptr->y][player_ptr->x].info & CAVE_GLOW)
323             set_superstealth(player_ptr, false);
324     }
325 }
326
327 /*!
328  * @brief (y1,x1) を含む全ての部屋を暗くする。 / Darken all rooms containing the given location
329  * @param player_ptr プレーヤーへの参照ポインタ
330  * @param y1 指定Y座標
331  * @param x1 指定X座標
332  */
333 void unlite_room(player_type *player_ptr, const POSITION y1, const POSITION x1)
334 {
335     // 暗くするマスを記録する配列。
336     std::vector<Pos2D> points;
337
338     floor_type *floor_ptr = player_ptr->current_floor_ptr;
339
340     // (y1,x1) を起点として暗くするマスを記録していく。
341     // 実質幅優先探索。
342     cave_temp_unlite_room_aux(player_ptr, points, y1, x1);
343     for (size_t i = 0; i < size(points); i++) {
344         const auto &point = points[i];
345         const POSITION y = point.y;
346         const POSITION x = point.x;
347
348         if (!cave_pass_dark_bold(floor_ptr, y, x))
349             continue;
350
351         cave_temp_unlite_room_aux(player_ptr, points, y + 1, x);
352         cave_temp_unlite_room_aux(player_ptr, points, y - 1, x);
353         cave_temp_unlite_room_aux(player_ptr, points, y, x + 1);
354         cave_temp_unlite_room_aux(player_ptr, points, y, x - 1);
355
356         cave_temp_unlite_room_aux(player_ptr, points, y + 1, x + 1);
357         cave_temp_unlite_room_aux(player_ptr, points, y - 1, x - 1);
358         cave_temp_unlite_room_aux(player_ptr, points, y - 1, x + 1);
359         cave_temp_unlite_room_aux(player_ptr, points, y + 1, x - 1);
360     }
361
362     // 記録したマスを実際に暗くする。
363     cave_temp_room_unlite(player_ptr, points);
364 }
365
366 /*!
367  * @brief スターライトの効果を発生させる
368  * @param player_ptr プレーヤーへの参照ポインタ
369  * @param magic 魔法による効果であればTRUE、スターライトの杖による効果であればFALSE
370  * @return 常にTRUE
371  */
372 bool starlight(player_type *player_ptr, bool magic)
373 {
374     if (!player_ptr->blind && !magic) {
375         msg_print(_("杖の先が明るく輝いた...", "The end of the staff glows brightly..."));
376     }
377
378     HIT_POINT num = damroll(5, 3);
379     int attempts;
380     POSITION y = 0, x = 0;
381     for (int k = 0; k < num; k++) {
382         attempts = 1000;
383
384         while (attempts--) {
385             scatter(player_ptr, &y, &x, player_ptr->y, player_ptr->x, 4, PROJECT_LOS);
386             if (!cave_has_flag_bold(player_ptr->current_floor_ptr, y, x, FF::PROJECT))
387                 continue;
388             if (!player_bold(player_ptr, y, x))
389                 break;
390         }
391
392         project(player_ptr, 0, 0, y, x, damroll(6 + player_ptr->lev / 8, 10), GF_LITE_WEAK,
393             (PROJECT_BEAM | PROJECT_THRU | PROJECT_GRID | PROJECT_KILL | PROJECT_LOS));
394     }
395
396     return true;
397 }
398
399 /*!
400  * @brief プレイヤー位置を中心にLITE_WEAK属性を通じた照明処理を行う / Hack -- call light around the player Affect all monsters in the projection radius
401  * @param player_ptr プレーヤーへの参照ポインタ
402  * @param dam 威力
403  * @param rad 効果半径
404  * @return 作用が実際にあった場合TRUEを返す
405  */
406 bool lite_area(player_type *player_ptr, HIT_POINT dam, POSITION rad)
407 {
408     if (d_info[player_ptr->dungeon_idx].flags.has(DF::DARKNESS)) {
409         msg_print(_("ダンジョンが光を吸収した。", "The darkness of this dungeon absorbs your light."));
410         return false;
411     }
412
413     if (!player_ptr->blind) {
414         msg_print(_("白い光が辺りを覆った。", "You are surrounded by a white light."));
415     }
416
417     BIT_FLAGS flg = PROJECT_GRID | PROJECT_KILL;
418     (void)project(player_ptr, 0, rad, player_ptr->y, player_ptr->x, dam, GF_LITE_WEAK, flg);
419
420     lite_room(player_ptr, player_ptr->y, player_ptr->x);
421
422     return true;
423 }
424
425 /*!
426  * @brief プレイヤー位置を中心にLITE_DARK属性を通じた消灯処理を行う / Hack -- call light around the player Affect all monsters in the projection radius
427  * @param player_ptr プレーヤーへの参照ポインタ
428  * @param dam 威力
429  * @param rad 効果半径
430  * @return 作用が実際にあった場合TRUEを返す
431  */
432 bool unlite_area(player_type *player_ptr, HIT_POINT dam, POSITION rad)
433 {
434     if (!player_ptr->blind) {
435         msg_print(_("暗闇が辺りを覆った。", "Darkness surrounds you."));
436     }
437
438     BIT_FLAGS flg = PROJECT_GRID | PROJECT_KILL;
439     (void)project(player_ptr, 0, rad, player_ptr->y, player_ptr->x, dam, GF_DARK_WEAK, flg);
440
441     unlite_room(player_ptr, player_ptr->y, player_ptr->x);
442
443     return true;
444 }
445
446 /*!
447  * @brief LITE_WEAK属性による光源ビーム処理
448  * @param player_ptr プレーヤーへの参照ポインタ
449  * @param dir 方向(5ならばグローバル変数 target_col/target_row の座標を目標にする)
450  * @param dam 威力
451  * @return 作用が実際にあった場合TRUEを返す
452  */
453 bool lite_line(player_type *player_ptr, DIRECTION dir, HIT_POINT dam)
454 {
455     BIT_FLAGS flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_KILL;
456     return (project_hook(player_ptr, GF_LITE_WEAK, dir, dam, flg));
457 }