OSDN Git Service

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