OSDN Git Service

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