1 #include "effect/effect-processor.h"
2 #include "core/stuff-handler.h"
3 #include "effect/attribute-types.h"
4 #include "effect/effect-characteristics.h"
5 #include "effect/effect-feature.h"
6 #include "effect/effect-item.h"
7 #include "effect/effect-monster.h"
8 #include "effect/effect-player.h"
9 #include "effect/spells-effect-util.h"
10 #include "floor/cave.h"
11 #include "floor/geometry.h"
12 #include "floor/line-of-sight.h"
13 #include "game-option/map-screen-options.h"
14 #include "game-option/special-options.h"
15 #include "grid/feature-flag-types.h"
16 #include "grid/grid.h"
17 #include "io/cursor.h"
18 #include "io/screen-util.h"
19 #include "main/sound-definitions-table.h"
20 #include "main/sound-of-music.h"
21 #include "monster-race/monster-race.h"
22 #include "monster-race/race-flags2.h"
23 #include "monster-race/race-indice-types.h"
24 #include "monster/monster-describer.h"
25 #include "monster/monster-description-types.h"
26 #include "monster/monster-info.h"
27 #include "pet/pet-fall-off.h"
28 #include "player/player-status.h"
29 #include "spell/range-calc.h"
30 #include "system/floor-type-definition.h"
31 #include "system/grid-type-definition.h"
32 #include "system/monster-race-definition.h"
33 #include "system/monster-type-definition.h"
34 #include "system/player-type-definition.h"
35 #include "target/projection-path-calculator.h"
36 #include "term/gameterm.h"
37 #include "util/bit-flags-calculator.h"
38 #include "view/display-messages.h"
41 * @brief 配置した鏡リストの次を取得する /
42 * Get another mirror. for SEEKER
43 * @param next_y 次の鏡のy座標を返す参照ポインタ
44 * @param next_x 次の鏡のx座標を返す参照ポインタ
45 * @param cury 現在の鏡のy座標
46 * @param curx 現在の鏡のx座標
48 static void next_mirror(PlayerType *player_ptr, POSITION *next_y, POSITION *next_x, POSITION cury, POSITION curx)
50 POSITION mirror_x[10], mirror_y[10]; /* 鏡はもっと少ない */
51 int mirror_num = 0; /* 鏡の数 */
52 for (POSITION x = 0; x < player_ptr->current_floor_ptr->width; x++) {
53 for (POSITION y = 0; y < player_ptr->current_floor_ptr->height; y++) {
54 if (player_ptr->current_floor_ptr->grid_array[y][x].is_mirror()) {
55 mirror_y[mirror_num] = y;
56 mirror_x[mirror_num] = x;
63 int num = randint0(mirror_num);
64 *next_y = mirror_y[num];
65 *next_x = mirror_x[num];
69 *next_y = cury + randint0(5) - 2;
70 *next_x = curx + randint0(5) - 2;
74 * @brief 汎用的なビーム/ボルト/ボール系処理のルーチン Generic
75 * "beam"/"bolt"/"ball" projection routine.
76 * @param who 魔法を発動したモンスター(0ならばプレイヤー) / Index of "source"
77 * monster (zero for "player")
78 * @param rad 効果半径(ビーム/ボルト = 0 / ボール = 1以上) / Radius of explosion
79 * (0 = beam/bolt, 1 to 9 = ball)
80 * @param y 目標Y座標 / Target y location (or location to travel "towards")
81 * @param x 目標X座標 / Target x location (or location to travel "towards")
82 * @param dam 基本威力 / Base damage roll to apply to affected monsters (or
84 * @param typ 効果属性 / Type of damage to apply to monsters (and objects)
85 * @param flag 効果フラグ / Extra bit flags (see PROJECT_xxxx)
86 * @param monspell 効果元のモンスター魔法ID
87 * @todo 似たような処理が山ほど並んでいる、何とかならないものか
88 * @todo 引数にそのまま再代入していてカオスすぎる。直すのは簡単ではない
90 ProjectResult project(PlayerType *player_ptr, const MONSTER_IDX who, POSITION rad, POSITION y, POSITION x, const HIT_POINT dam,
91 const AttributeType typ, BIT_FLAGS flag)
103 bool blind = player_ptr->blind != 0;
104 bool old_hide = false;
106 uint16_t path_g[512];
111 POSITION gm_rad = rad;
113 GAME_TEXT who_name[MAX_NLEN];
114 bool see_s_msg = true;
118 monster_target_y = player_ptr->y;
119 monster_target_x = player_ptr->x;
123 if (flag & (PROJECT_JUMP)) {
126 flag &= ~(PROJECT_JUMP);
128 } else if (who <= 0) {
131 } else if (who > 0) {
132 x1 = player_ptr->current_floor_ptr->m_list[who].fx;
133 y1 = player_ptr->current_floor_ptr->m_list[who].fy;
134 monster_desc(player_ptr, who_name, &player_ptr->current_floor_ptr->m_list[who], MD_WRONGDOER_NAME);
145 if (flag & (PROJECT_THRU)) {
146 if ((x1 == x2) && (y1 == y2)) {
147 flag &= ~(PROJECT_THRU);
154 if (flag & PROJECT_HIDE)
156 flag |= PROJECT_HIDE;
159 for (dist = 0; dist < 32; dist++)
165 if (flag & (PROJECT_BEAM)) {
172 case AttributeType::LITE:
173 case AttributeType::LITE_WEAK:
174 if (breath || (flag & PROJECT_BEAM))
175 flag |= (PROJECT_LOS);
177 case AttributeType::DISINTEGRATE:
178 flag |= (PROJECT_GRID);
179 if (breath || (flag & PROJECT_BEAM))
180 flag |= (PROJECT_DISI);
186 /* Calculate the projection path */
187 path_n = projection_path(player_ptr, path_g, (project_length ? project_length : get_max_range(player_ptr)), y1, x1, y2, x2, flag);
188 handle_stuff(player_ptr);
190 if (typ == AttributeType::SEEKER) {
196 for (int i = 0; i < path_n; ++i) {
199 POSITION ny = get_grid_y(path_g[i]);
200 POSITION nx = get_grid_x(path_g[i]);
207 if (delay_factor > 0) {
208 if (!blind && !(flag & (PROJECT_HIDE))) {
209 if (panel_contains(y, x) && player_has_los_bold(player_ptr, y, x)) {
210 uint16_t p = bolt_pict(oy, ox, y, x, typ);
211 TERM_COLOR a = PICT_A(p);
212 SYMBOL_CODE c = PICT_C(p);
213 print_rel(player_ptr, c, a, y, x);
214 move_cursor_relative(y, x);
216 term_xtra(TERM_XTRA_DELAY, delay_factor);
217 lite_spot(player_ptr, y, x);
219 if (flag & (PROJECT_BEAM)) {
220 p = bolt_pict(y, x, y, x, typ);
223 print_rel(player_ptr, c, a, y, x);
228 term_xtra(TERM_XTRA_DELAY, delay_factor);
233 if (affect_item(player_ptr, 0, 0, y, x, dam, AttributeType::SEEKER))
235 if (!player_ptr->current_floor_ptr->grid_array[y][x].is_mirror())
238 monster_target_y = y;
239 monster_target_x = x;
240 remove_mirror(player_ptr, y, x);
241 next_mirror(player_ptr, &oy, &ox, y, x);
242 path_n = i + projection_path(player_ptr, &(path_g[i + 1]), (project_length ? project_length : get_max_range(player_ptr)), y, x, oy, ox, flag);
243 for (j = last_i; j <= i; j++) {
244 y = get_grid_y(path_g[j]);
245 x = get_grid_x(path_g[j]);
246 if (affect_monster(player_ptr, 0, 0, y, x, dam, AttributeType::SEEKER, flag, true))
248 if (!who && (project_m_n == 1) && !jump && (player_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx > 0)) {
249 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[player_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx];
251 if (!player_ptr->hallucinated)
252 monster_race_track(player_ptr, m_ptr->ap_r_idx);
253 health_track(player_ptr, player_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx);
257 (void)affect_feature(player_ptr, 0, 0, y, x, dam, AttributeType::SEEKER);
263 for (int i = last_i; i < path_n; i++) {
265 py = get_grid_y(path_g[i]);
266 px = get_grid_x(path_g[i]);
267 if (affect_monster(player_ptr, 0, 0, py, px, dam, AttributeType::SEEKER, flag, true))
269 if (!who && (project_m_n == 1) && !jump) {
270 if (player_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx > 0) {
271 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[player_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx];
274 if (!player_ptr->hallucinated)
275 monster_race_track(player_ptr, m_ptr->ap_r_idx);
276 health_track(player_ptr, player_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx);
281 (void)affect_feature(player_ptr, 0, 0, py, px, dam, AttributeType::SEEKER);
285 } else if (typ == AttributeType::SUPER_RAY) {
291 for (int i = 0; i < path_n; ++i) {
294 POSITION ny = get_grid_y(path_g[i]);
295 POSITION nx = get_grid_x(path_g[i]);
302 if (delay_factor > 0) {
303 if (panel_contains(y, x) && player_has_los_bold(player_ptr, y, x)) {
307 p = bolt_pict(oy, ox, y, x, typ);
310 print_rel(player_ptr, c, a, y, x);
311 move_cursor_relative(y, x);
313 term_xtra(TERM_XTRA_DELAY, delay_factor);
314 lite_spot(player_ptr, y, x);
316 if (flag & (PROJECT_BEAM)) {
317 p = bolt_pict(y, x, y, x, typ);
320 print_rel(player_ptr, c, a, y, x);
325 term_xtra(TERM_XTRA_DELAY, delay_factor);
330 if (affect_item(player_ptr, 0, 0, y, x, dam, AttributeType::SUPER_RAY))
332 if (!cave_has_flag_bold(player_ptr->current_floor_ptr, y, x, FloorFeatureType::PROJECT)) {
338 if (player_ptr->current_floor_ptr->grid_array[y][x].is_mirror() && !second_step) {
339 monster_target_y = y;
340 monster_target_x = x;
341 remove_mirror(player_ptr, y, x);
342 for (j = 0; j <= i; j++) {
343 y = get_grid_y(path_g[j]);
344 x = get_grid_x(path_g[j]);
345 (void)affect_feature(player_ptr, 0, 0, y, x, dam, AttributeType::SUPER_RAY);
350 path_n += projection_path(
351 player_ptr, &(path_g[path_n + 1]), (project_length ? project_length : get_max_range(player_ptr)), y, x, y - 1, x - 1, flag);
352 path_n += projection_path(player_ptr, &(path_g[path_n + 1]), (project_length ? project_length : get_max_range(player_ptr)), y, x, y - 1, x, flag);
353 path_n += projection_path(
354 player_ptr, &(path_g[path_n + 1]), (project_length ? project_length : get_max_range(player_ptr)), y, x, y - 1, x + 1, flag);
355 path_n += projection_path(player_ptr, &(path_g[path_n + 1]), (project_length ? project_length : get_max_range(player_ptr)), y, x, y, x - 1, flag);
356 path_n += projection_path(player_ptr, &(path_g[path_n + 1]), (project_length ? project_length : get_max_range(player_ptr)), y, x, y, x + 1, flag);
357 path_n += projection_path(
358 player_ptr, &(path_g[path_n + 1]), (project_length ? project_length : get_max_range(player_ptr)), y, x, y + 1, x - 1, flag);
359 path_n += projection_path(player_ptr, &(path_g[path_n + 1]), (project_length ? project_length : get_max_range(player_ptr)), y, x, y + 1, x, flag);
360 path_n += projection_path(
361 player_ptr, &(path_g[path_n + 1]), (project_length ? project_length : get_max_range(player_ptr)), y, x, y + 1, x + 1, flag);
365 for (int i = 0; i < path_n; i++) {
366 POSITION py = get_grid_y(path_g[i]);
367 POSITION px = get_grid_x(path_g[i]);
368 (void)affect_monster(player_ptr, 0, 0, py, px, dam, AttributeType::SUPER_RAY, flag, true);
369 if (!who && (project_m_n == 1) && !jump) {
370 if (player_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx > 0) {
371 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[player_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx];
374 if (!player_ptr->hallucinated)
375 monster_race_track(player_ptr, m_ptr->ap_r_idx);
376 health_track(player_ptr, player_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx);
381 (void)affect_feature(player_ptr, 0, 0, py, px, dam, AttributeType::SUPER_RAY);
388 for (k = 0; k < path_n; ++k) {
391 POSITION ny = get_grid_y(path_g[k]);
392 POSITION nx = get_grid_x(path_g[k]);
393 if (flag & PROJECT_DISI) {
394 if (cave_stop_disintegration(player_ptr->current_floor_ptr, ny, nx) && (rad > 0))
396 } else if (flag & PROJECT_LOS) {
397 if (!cave_los_bold(player_ptr->current_floor_ptr, ny, nx) && (rad > 0))
400 if (!cave_has_flag_bold(player_ptr->current_floor_ptr, ny, nx, FloorFeatureType::PROJECT) && (rad > 0))
406 if (flag & (PROJECT_BEAM)) {
412 if (delay_factor > 0) {
413 if (!blind && !(flag & (PROJECT_HIDE | PROJECT_FAST))) {
414 if (panel_contains(y, x) && player_has_los_bold(player_ptr, y, x)) {
418 p = bolt_pict(oy, ox, y, x, typ);
421 print_rel(player_ptr, c, a, y, x);
422 move_cursor_relative(y, x);
424 term_xtra(TERM_XTRA_DELAY, delay_factor);
425 lite_spot(player_ptr, y, x);
427 if (flag & (PROJECT_BEAM)) {
428 p = bolt_pict(y, x, y, x, typ);
431 print_rel(player_ptr, c, a, y, x);
436 term_xtra(TERM_XTRA_DELAY, delay_factor);
445 if (breath && !path_n) {
449 flag &= ~(PROJECT_HIDE);
456 int dist_hack = dist;
459 /* If we found a "target", explode there */
460 if (dist <= get_max_range(player_ptr)) {
461 if ((flag & (PROJECT_BEAM)) && (grids > 0))
465 * Create a conical breath attack
474 flag &= ~(PROJECT_HIDE);
475 breath_shape(player_ptr, path_g, dist, &grids, gx, gy, gm, &gm_rad, rad, y1, x1, by, bx, typ);
477 for (dist = 0; dist <= rad; dist++) {
478 for (y = by - dist; y <= by + dist; y++) {
479 for (x = bx - dist; x <= bx + dist; x++) {
480 if (!in_bounds2(player_ptr->current_floor_ptr, y, x))
482 if (distance(by, bx, y, x) != dist)
486 case AttributeType::LITE:
487 case AttributeType::LITE_WEAK:
488 if (!los(player_ptr, by, bx, y, x))
491 case AttributeType::DISINTEGRATE:
492 if (!in_disintegration_range(player_ptr->current_floor_ptr, by, bx, y, x))
496 if (!projectable(player_ptr, by, bx, y, x))
507 gm[dist + 1] = grids;
515 if (!blind && !(flag & (PROJECT_HIDE)) && (delay_factor > 0)) {
516 for (int t = 0; t <= gm_rad; t++) {
517 for (int i = gm[t]; i < gm[t + 1]; i++) {
520 if (panel_contains(y, x) && player_has_los_bold(player_ptr, y, x)) {
525 p = bolt_pict(y, x, y, x, typ);
528 print_rel(player_ptr, c, a, y, x);
532 move_cursor_relative(by, bx);
534 if (visual || drawn) {
535 term_xtra(TERM_XTRA_DELAY, delay_factor);
540 for (int i = 0; i < grids; i++) {
543 if (panel_contains(y, x) && player_has_los_bold(player_ptr, y, x)) {
544 lite_spot(player_ptr, y, x);
548 move_cursor_relative(by, bx);
553 update_creature(player_ptr);
555 if (flag & PROJECT_KILL) {
556 see_s_msg = (who > 0) ? is_seen(player_ptr, &player_ptr->current_floor_ptr->m_list[who])
557 : (!who ? true : (player_can_see_bold(player_ptr, y1, x1) && projectable(player_ptr, player_ptr->y, player_ptr->x, y1, x1)));
560 if (flag & (PROJECT_GRID)) {
562 for (int i = 0; i < grids; i++) {
563 if (gm[dist + 1] == i)
568 int d = dist_to_line(y, x, y1, x1, by, bx);
569 if (affect_feature(player_ptr, who, d, y, x, dam, typ))
572 if (affect_feature(player_ptr, who, dist, y, x, dam, typ))
578 update_creature(player_ptr);
579 if (flag & (PROJECT_ITEM)) {
581 for (int i = 0; i < grids; i++) {
582 if (gm[dist + 1] == i)
588 int d = dist_to_line(y, x, y1, x1, by, bx);
589 if (affect_item(player_ptr, who, d, y, x, dam, typ))
592 if (affect_item(player_ptr, who, dist, y, x, dam, typ))
598 if (flag & (PROJECT_KILL)) {
603 for (int i = 0; i < grids; i++) {
605 if (gm[dist + 1] == i)
611 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[player_ptr->current_floor_ptr->grid_array[y][x].m_idx];
612 monster_race *ref_ptr = &r_info[m_ptr->r_idx];
613 if ((flag & PROJECT_REFLECTABLE) && player_ptr->current_floor_ptr->grid_array[y][x].m_idx && (ref_ptr->flags2 & RF2_REFLECTING) && ((player_ptr->current_floor_ptr->grid_array[y][x].m_idx != player_ptr->riding) || !(flag & PROJECT_PLAYER)) && (!who || dist_hack > 1) && !one_in_(10)) {
615 int max_attempts = 10;
617 t_y = y_saver - 1 + randint1(3);
618 t_x = x_saver - 1 + randint1(3);
620 } while (max_attempts && in_bounds2u(player_ptr->current_floor_ptr, t_y, t_x) && !projectable(player_ptr, y, x, t_y, t_x));
622 if (max_attempts < 1) {
627 if (is_seen(player_ptr, m_ptr)) {
628 sound(SOUND_REFLECT);
629 if ((m_ptr->r_idx == MON_KENSHIROU) || (m_ptr->r_idx == MON_RAOU))
630 msg_print(_("「北斗神拳奥義・二指真空把!」", "The attack bounces!"));
631 else if (m_ptr->r_idx == MON_DIO)
632 msg_print(_("ディオ・ブランドーは指一本で攻撃を弾き返した!", "The attack bounces!"));
634 msg_print(_("攻撃は跳ね返った!", "The attack bounces!"));
635 } else if (who <= 0) {
636 sound(SOUND_REFLECT);
639 if (is_original_ap_and_seen(player_ptr, m_ptr))
640 ref_ptr->r_flags2 |= RF2_REFLECTING;
642 if (player_bold(player_ptr, y, x) || one_in_(2))
643 flag &= ~(PROJECT_PLAYER);
645 flag |= PROJECT_PLAYER;
647 project(player_ptr, player_ptr->current_floor_ptr->grid_array[y][x].m_idx, 0, t_y, t_x, dam, typ, flag);
652 /* Find the closest point in the blast */
654 effective_dist = dist_to_line(y, x, y1, x1, by, bx);
656 effective_dist = dist;
659 if (player_ptr->riding && player_bold(player_ptr, y, x)) {
660 if (flag & PROJECT_PLAYER) {
661 if (flag & (PROJECT_BEAM | PROJECT_REFLECTABLE | PROJECT_AIMED)) {
663 * A beam or bolt is well aimed
665 * So don't affects the mount.
670 * The spell is not well aimed,
671 * So partly affect the mount too.
678 * This grid is the original target.
679 * Or aimed on your horse.
681 else if (((y == y2) && (x == x2)) || (flag & PROJECT_AIMED)) {
682 /* Hit the mount with full damage */
686 * Otherwise this grid is not the
687 * original target, it means that line
688 * of fire is obstructed by this
692 * A beam or bolt will hit either
693 * player or mount. Choose randomly.
695 else if (flag & (PROJECT_BEAM | PROJECT_REFLECTABLE)) {
697 /* Hit the mount with full damage */
699 flag |= PROJECT_PLAYER;
705 * The spell is not well aimed, so
706 * partly affect both player and
714 if (affect_monster(player_ptr, who, effective_dist, y, x, dam, typ, flag, see_s_msg))
718 /* Player affected one monster (without "jumping") */
719 if (!who && (project_m_n == 1) && !jump) {
722 if (player_ptr->current_floor_ptr->grid_array[y][x].m_idx > 0) {
723 auto *m_ptr = &player_ptr->current_floor_ptr->m_list[player_ptr->current_floor_ptr->grid_array[y][x].m_idx];
726 if (!player_ptr->hallucinated)
727 monster_race_track(player_ptr, m_ptr->ap_r_idx);
728 health_track(player_ptr, player_ptr->current_floor_ptr->grid_array[y][x].m_idx);
734 if (flag & (PROJECT_KILL)) {
736 for (int i = 0; i < grids; i++) {
738 if (gm[dist + 1] == i)
743 if (!player_bold(player_ptr, y, x))
746 /* Find the closest point in the blast */
748 effective_dist = dist_to_line(y, x, y1, x1, by, bx);
750 effective_dist = dist;
753 if (player_ptr->riding) {
754 if (flag & PROJECT_PLAYER) {
755 /* Hit the player with full damage */
759 * Hack -- When this grid was not the
760 * original target, a beam or bolt
761 * would hit either player or mount,
762 * and should be choosen randomly.
764 * But already choosen to hit the
765 * mount at this point.
767 * Or aimed on your horse.
769 else if (flag & (PROJECT_BEAM | PROJECT_REFLECTABLE | PROJECT_AIMED)) {
771 * A beam or bolt is well aimed
773 * So don't affects the player.
778 * The spell is not well aimed,
779 * So partly affect the player too.
785 if (affect_player(who, player_ptr, who_name, effective_dist, y, x, dam, typ, flag, project)) {
787 res.affected_player = true;
792 if (player_ptr->riding) {
793 GAME_TEXT m_name[MAX_NLEN];
794 monster_desc(player_ptr, m_name, &player_ptr->current_floor_ptr->m_list[player_ptr->riding], 0);
795 if (rakubadam_m > 0) {
796 if (process_fall_off_horse(player_ptr, rakubadam_m, false)) {
797 msg_format(_("%^sに振り落とされた!", "%^s has thrown you off!"), m_name);
801 if (player_ptr->riding && rakubadam_p > 0) {
802 if (process_fall_off_horse(player_ptr, rakubadam_p, false)) {
803 msg_format(_("%^sから落ちてしまった!", "You have fallen from %s."), m_name);