OSDN Git Service

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