OSDN Git Service

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