OSDN Git Service

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