1 #include "spell/process-effect.h"
2 #include "core/stuff-handler.h"
3 #include "effect/effect-characteristics.h"
4 #include "effect/effect-feature.h"
5 #include "effect/effect-item.h"
6 #include "effect/effect-monster.h"
7 #include "effect/effect-player.h"
8 #include "effect/spells-effect-util.h"
9 #include "floor/floor.h"
10 #include "main/sound-definitions-table.h"
11 #include "monster-race/race-flags2.h"
12 #include "monster-race/race-indice-types.h"
13 #include "monster/monster-describer.h"
14 #include "monster/monster-description-types.h"
15 #include "monster/monster-info.h"
16 #include "pet/pet-fall-off.h"
17 #include "spell/spells-type.h"
18 #include "spell/range-calc.h"
19 #include "term/gameterm.h"
20 #include "view/display-main-window.h" // 暫定、後で消すかも.
23 * @brief 配置した鏡リストの次を取得する /
24 * Get another mirror. for SEEKER
25 * @param next_y 次の鏡のy座標を返す参照ポインタ
26 * @param next_x 次の鏡のx座標を返す参照ポインタ
27 * @param cury 現在の鏡のy座標
28 * @param curx 現在の鏡のx座標
30 static void next_mirror(player_type *creature_ptr, POSITION *next_y, POSITION *next_x, POSITION cury, POSITION curx)
32 POSITION mirror_x[10], mirror_y[10]; /* 鏡はもっと少ない */
33 int mirror_num = 0; /* 鏡の数 */
34 for (POSITION x = 0; x < creature_ptr->current_floor_ptr->width; x++) {
35 for (POSITION y = 0; y < creature_ptr->current_floor_ptr->height; y++) {
36 if (is_mirror_grid(&creature_ptr->current_floor_ptr->grid_array[y][x])) {
37 mirror_y[mirror_num] = y;
38 mirror_x[mirror_num] = x;
45 int num = randint0(mirror_num);
46 *next_y = mirror_y[num];
47 *next_x = mirror_x[num];
51 *next_y = cury + randint0(5) - 2;
52 *next_x = curx + randint0(5) - 2;
56 * todo 似たような処理が山ほど並んでいる、何とかならないものか
57 * todo 引数にそのまま再代入していてカオスすぎる。直すのは簡単ではない
58 * @brief 汎用的なビーム/ボルト/ボール系処理のルーチン Generic
59 * "beam"/"bolt"/"ball" projection routine.
60 * @param who 魔法を発動したモンスター(0ならばプレイヤー) / Index of "source"
61 * monster (zero for "player")
62 * @param rad 効果半径(ビーム/ボルト = 0 / ボール = 1以上) / Radius of explosion
63 * (0 = beam/bolt, 1 to 9 = ball)
64 * @param y 目標Y座標 / Target y location (or location to travel "towards")
65 * @param x 目標X座標 / Target x location (or location to travel "towards")
66 * @param dam 基本威力 / Base damage roll to apply to affected monsters (or
68 * @param typ 効果属性 / Type of damage to apply to monsters (and objects)
69 * @param flag 効果フラグ / Extra bit flags (see PROJECT_xxxx)
70 * @param monspell 効果元のモンスター魔法ID
71 * @return 何か一つでも効力があればTRUEを返す / TRUE if any "effects" of the
72 * projection were observed, else FALSE
74 bool project(player_type *caster_ptr, MONSTER_IDX who, POSITION rad, POSITION y, POSITION x, HIT_POINT dam, EFFECT_ID typ, BIT_FLAGS flag, int monspell)
80 /* For reflecting monsters */
83 int msec = delay_factor * delay_factor * delay_factor;
88 bool blind = caster_ptr->blind != 0;
89 bool old_hide = FALSE;
96 POSITION gm_rad = rad;
98 GAME_TEXT who_name[MAX_NLEN];
99 bool see_s_msg = TRUE;
103 monster_target_y = caster_ptr->y;
104 monster_target_x = caster_ptr->x;
106 if (flag & (PROJECT_JUMP)) {
109 flag &= ~(PROJECT_JUMP);
111 } else if (who <= 0) {
114 } else if (who > 0) {
115 x1 = caster_ptr->current_floor_ptr->m_list[who].fx;
116 y1 = caster_ptr->current_floor_ptr->m_list[who].fy;
117 monster_desc(caster_ptr, who_name, &caster_ptr->current_floor_ptr->m_list[who], MD_WRONGDOER_NAME);
128 if (flag & (PROJECT_THRU)) {
129 if ((x1 == x2) && (y1 == y2)) {
130 flag &= ~(PROJECT_THRU);
137 if (flag & PROJECT_HIDE)
139 flag |= PROJECT_HIDE;
142 for (dist = 0; dist < 32; dist++)
148 if (flag & (PROJECT_BEAM)) {
157 if (breath || (flag & PROJECT_BEAM))
158 flag |= (PROJECT_LOS);
160 case GF_DISINTEGRATE:
161 flag |= (PROJECT_GRID);
162 if (breath || (flag & PROJECT_BEAM))
163 flag |= (PROJECT_DISI);
167 /* Calculate the projection path */
168 path_n = project_path(caster_ptr, path_g, (project_length ? project_length : MAX_RANGE), y1, x1, y2, x2, flag);
169 handle_stuff(caster_ptr);
171 if (typ == GF_SEEKER) {
177 for (int i = 0; i < path_n; ++i) {
180 POSITION ny = GRID_Y(path_g[i]);
181 POSITION nx = GRID_X(path_g[i]);
188 if (!blind && !(flag & (PROJECT_HIDE))) {
189 if (panel_contains(y, x) && player_has_los_bold(caster_ptr, y, x)) {
190 u16b p = bolt_pict(oy, ox, y, x, typ);
191 TERM_COLOR a = PICT_A(p);
192 SYMBOL_CODE c = PICT_C(p);
193 print_rel(caster_ptr, c, a, y, x);
194 move_cursor_relative(y, x);
196 Term_xtra(TERM_XTRA_DELAY, msec);
197 lite_spot(caster_ptr, y, x);
199 if (flag & (PROJECT_BEAM)) {
200 p = bolt_pict(y, x, y, x, typ);
203 print_rel(caster_ptr, c, a, y, x);
208 Term_xtra(TERM_XTRA_DELAY, msec);
212 if (affect_item(caster_ptr, 0, 0, y, x, dam, GF_SEEKER))
214 if (!is_mirror_grid(&caster_ptr->current_floor_ptr->grid_array[y][x]))
217 monster_target_y = y;
218 monster_target_x = x;
219 remove_mirror(caster_ptr, y, x);
220 next_mirror(caster_ptr, &oy, &ox, y, x);
221 path_n = i + project_path(caster_ptr, &(path_g[i + 1]), (project_length ? project_length : MAX_RANGE), y, x, oy, ox, flag);
222 for (j = last_i; j <= i; j++) {
223 y = GRID_Y(path_g[j]);
224 x = GRID_X(path_g[j]);
225 if (affect_monster(caster_ptr, 0, 0, y, x, dam, GF_SEEKER, flag, TRUE))
227 if (!who && (project_m_n == 1) && !jump && (caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx > 0)) {
228 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx];
230 if (!caster_ptr->image)
231 monster_race_track(caster_ptr, m_ptr->ap_r_idx);
232 health_track(caster_ptr, caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx);
236 (void)affect_feature(caster_ptr, 0, 0, y, x, dam, GF_SEEKER);
242 for (int i = last_i; i < path_n; i++) {
244 py = GRID_Y(path_g[i]);
245 px = GRID_X(path_g[i]);
246 if (affect_monster(caster_ptr, 0, 0, py, px, dam, GF_SEEKER, flag, TRUE))
248 if (!who && (project_m_n == 1) && !jump) {
249 if (caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx > 0) {
250 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx];
253 if (!caster_ptr->image)
254 monster_race_track(caster_ptr, m_ptr->ap_r_idx);
255 health_track(caster_ptr, caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx);
260 (void)affect_feature(caster_ptr, 0, 0, py, px, dam, GF_SEEKER);
264 } else if (typ == GF_SUPER_RAY) {
270 for (int i = 0; i < path_n; ++i) {
273 POSITION ny = GRID_Y(path_g[i]);
274 POSITION nx = GRID_X(path_g[i]);
281 if (panel_contains(y, x) && player_has_los_bold(caster_ptr, y, x)) {
285 p = bolt_pict(oy, ox, y, x, typ);
288 print_rel(caster_ptr, c, a, y, x);
289 move_cursor_relative(y, x);
291 Term_xtra(TERM_XTRA_DELAY, msec);
292 lite_spot(caster_ptr, y, x);
294 if (flag & (PROJECT_BEAM)) {
295 p = bolt_pict(y, x, y, x, typ);
298 print_rel(caster_ptr, c, a, y, x);
303 Term_xtra(TERM_XTRA_DELAY, msec);
307 if (affect_item(caster_ptr, 0, 0, y, x, dam, GF_SUPER_RAY))
309 if (!cave_have_flag_bold(caster_ptr->current_floor_ptr, y, x, FF_PROJECT)) {
315 if (is_mirror_grid(&caster_ptr->current_floor_ptr->grid_array[y][x]) && !second_step) {
316 monster_target_y = y;
317 monster_target_x = x;
318 remove_mirror(caster_ptr, y, x);
319 for (j = 0; j <= i; j++) {
320 y = GRID_Y(path_g[j]);
321 x = GRID_X(path_g[j]);
322 (void)affect_feature(caster_ptr, 0, 0, y, x, dam, GF_SUPER_RAY);
327 path_n += project_path(caster_ptr, &(path_g[path_n + 1]), (project_length ? project_length : MAX_RANGE), y, x, y - 1, x - 1, flag);
328 path_n += project_path(caster_ptr, &(path_g[path_n + 1]), (project_length ? project_length : MAX_RANGE), y, x, y - 1, x, flag);
329 path_n += project_path(caster_ptr, &(path_g[path_n + 1]), (project_length ? project_length : MAX_RANGE), y, x, y - 1, x + 1, flag);
330 path_n += project_path(caster_ptr, &(path_g[path_n + 1]), (project_length ? project_length : MAX_RANGE), y, x, y, x - 1, flag);
331 path_n += project_path(caster_ptr, &(path_g[path_n + 1]), (project_length ? project_length : MAX_RANGE), y, x, y, x + 1, flag);
332 path_n += project_path(caster_ptr, &(path_g[path_n + 1]), (project_length ? project_length : MAX_RANGE), y, x, y + 1, x - 1, flag);
333 path_n += project_path(caster_ptr, &(path_g[path_n + 1]), (project_length ? project_length : MAX_RANGE), y, x, y + 1, x, flag);
334 path_n += project_path(caster_ptr, &(path_g[path_n + 1]), (project_length ? project_length : MAX_RANGE), y, x, y + 1, x + 1, flag);
338 for (int i = 0; i < path_n; i++) {
339 POSITION py = GRID_Y(path_g[i]);
340 POSITION px = GRID_X(path_g[i]);
341 (void)affect_monster(caster_ptr, 0, 0, py, px, dam, GF_SUPER_RAY, flag, TRUE);
342 if (!who && (project_m_n == 1) && !jump) {
343 if (caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx > 0) {
344 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx];
347 if (!caster_ptr->image)
348 monster_race_track(caster_ptr, m_ptr->ap_r_idx);
349 health_track(caster_ptr, caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx);
354 (void)affect_feature(caster_ptr, 0, 0, py, px, dam, GF_SUPER_RAY);
361 for (k = 0; k < path_n; ++k) {
364 POSITION ny = GRID_Y(path_g[k]);
365 POSITION nx = GRID_X(path_g[k]);
366 if (flag & PROJECT_DISI) {
367 if (cave_stop_disintegration(caster_ptr->current_floor_ptr, ny, nx) && (rad > 0))
369 } else if (flag & PROJECT_LOS) {
370 if (!cave_los_bold(caster_ptr->current_floor_ptr, ny, nx) && (rad > 0))
373 if (!cave_have_flag_bold(caster_ptr->current_floor_ptr, ny, nx, FF_PROJECT) && (rad > 0))
379 if (flag & (PROJECT_BEAM)) {
385 if (!blind && !(flag & (PROJECT_HIDE | PROJECT_FAST))) {
386 if (panel_contains(y, x) && player_has_los_bold(caster_ptr, y, x)) {
390 p = bolt_pict(oy, ox, y, x, typ);
393 print_rel(caster_ptr, c, a, y, x);
394 move_cursor_relative(y, x);
396 Term_xtra(TERM_XTRA_DELAY, msec);
397 lite_spot(caster_ptr, y, x);
399 if (flag & (PROJECT_BEAM)) {
400 p = bolt_pict(y, x, y, x, typ);
403 print_rel(caster_ptr, c, a, y, x);
408 Term_xtra(TERM_XTRA_DELAY, msec);
416 if (breath && !path_n) {
420 flag &= ~(PROJECT_HIDE);
427 int dist_hack = dist;
430 /* If we found a "target", explode there */
431 if (dist <= MAX_RANGE) {
432 if ((flag & (PROJECT_BEAM)) && (grids > 0))
436 * Create a conical breath attack
445 flag &= ~(PROJECT_HIDE);
446 breath_shape(caster_ptr, path_g, dist, &grids, gx, gy, gm, &gm_rad, rad, y1, x1, by, bx, typ);
448 for (dist = 0; dist <= rad; dist++) {
449 for (y = by - dist; y <= by + dist; y++) {
450 for (x = bx - dist; x <= bx + dist; x++) {
451 if (!in_bounds2(caster_ptr->current_floor_ptr, y, x))
453 if (distance(by, bx, y, x) != dist)
459 if (!los(caster_ptr, by, bx, y, x))
462 case GF_DISINTEGRATE:
463 if (!in_disintegration_range(caster_ptr->current_floor_ptr, by, bx, y, x))
467 if (!projectable(caster_ptr, by, bx, y, x))
478 gm[dist + 1] = grids;
486 if (!blind && !(flag & (PROJECT_HIDE))) {
487 for (int t = 0; t <= gm_rad; t++) {
488 for (int i = gm[t]; i < gm[t + 1]; i++) {
491 if (panel_contains(y, x) && player_has_los_bold(caster_ptr, y, x)) {
496 p = bolt_pict(y, x, y, x, typ);
499 print_rel(caster_ptr, c, a, y, x);
503 move_cursor_relative(by, bx);
505 if (visual || drawn) {
506 Term_xtra(TERM_XTRA_DELAY, msec);
511 for (int i = 0; i < grids; i++) {
514 if (panel_contains(y, x) && player_has_los_bold(caster_ptr, y, x)) {
515 lite_spot(caster_ptr, y, x);
519 move_cursor_relative(by, bx);
524 update_creature(caster_ptr);
526 if (flag & PROJECT_KILL) {
527 see_s_msg = (who > 0) ? is_seen(&caster_ptr->current_floor_ptr->m_list[who])
528 : (!who ? TRUE : (player_can_see_bold(caster_ptr, y1, x1) && projectable(caster_ptr, caster_ptr->y, caster_ptr->x, y1, x1)));
531 if (flag & (PROJECT_GRID)) {
533 for (int i = 0; i < grids; i++) {
534 if (gm[dist + 1] == i)
539 int d = dist_to_line(y, x, y1, x1, by, bx);
540 if (affect_feature(caster_ptr, who, d, y, x, dam, typ))
543 if (affect_feature(caster_ptr, who, dist, y, x, dam, typ))
549 update_creature(caster_ptr);
550 if (flag & (PROJECT_ITEM)) {
552 for (int i = 0; i < grids; i++) {
553 if (gm[dist + 1] == i)
559 int d = dist_to_line(y, x, y1, x1, by, bx);
560 if (affect_item(caster_ptr, who, d, y, x, dam, typ))
563 if (affect_item(caster_ptr, who, dist, y, x, dam, typ))
569 if (flag & (PROJECT_KILL)) {
574 for (int i = 0; i < grids; i++) {
576 if (gm[dist + 1] == i)
582 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[caster_ptr->current_floor_ptr->grid_array[y][x].m_idx];
583 monster_race *ref_ptr = &r_info[m_ptr->r_idx];
584 if ((flag & PROJECT_REFLECTABLE) && caster_ptr->current_floor_ptr->grid_array[y][x].m_idx && (ref_ptr->flags2 & RF2_REFLECTING)
585 && ((caster_ptr->current_floor_ptr->grid_array[y][x].m_idx != caster_ptr->riding) || !(flag & PROJECT_PLAYER)) && (!who || dist_hack > 1)
588 int max_attempts = 10;
590 t_y = y_saver - 1 + randint1(3);
591 t_x = x_saver - 1 + randint1(3);
593 } while (max_attempts && in_bounds2u(caster_ptr->current_floor_ptr, t_y, t_x) && !projectable(caster_ptr, y, x, t_y, t_x));
595 if (max_attempts < 1) {
600 sound(SOUND_REFLECT);
601 if (is_seen(m_ptr)) {
602 if ((m_ptr->r_idx == MON_KENSHIROU) || (m_ptr->r_idx == MON_RAOU))
603 msg_print(_("「北斗神拳奥義・二指真空把!」", "The attack bounces!"));
604 else if (m_ptr->r_idx == MON_DIO)
605 msg_print(_("ディオ・ブランドーは指一本で攻撃を弾き返した!", "The attack bounces!"));
607 msg_print(_("攻撃は跳ね返った!", "The attack bounces!"));
610 if (is_original_ap_and_seen(caster_ptr, m_ptr))
611 ref_ptr->r_flags2 |= RF2_REFLECTING;
613 if (player_bold(caster_ptr, y, x) || one_in_(2))
614 flag &= ~(PROJECT_PLAYER);
616 flag |= PROJECT_PLAYER;
618 project(caster_ptr, caster_ptr->current_floor_ptr->grid_array[y][x].m_idx, 0, t_y, t_x, dam, typ, flag, monspell);
623 /* Find the closest point in the blast */
625 effective_dist = dist_to_line(y, x, y1, x1, by, bx);
627 effective_dist = dist;
630 if (caster_ptr->riding && player_bold(caster_ptr, y, x)) {
631 if (flag & PROJECT_PLAYER) {
632 if (flag & (PROJECT_BEAM | PROJECT_REFLECTABLE | PROJECT_AIMED)) {
634 * A beam or bolt is well aimed
636 * So don't affects the mount.
641 * The spell is not well aimed,
642 * So partly affect the mount too.
649 * This grid is the original target.
650 * Or aimed on your horse.
652 else if (((y == y2) && (x == x2)) || (flag & PROJECT_AIMED)) {
653 /* Hit the mount with full damage */
657 * Otherwise this grid is not the
658 * original target, it means that line
659 * of fire is obstructed by this
663 * A beam or bolt will hit either
664 * player or mount. Choose randomly.
666 else if (flag & (PROJECT_BEAM | PROJECT_REFLECTABLE)) {
668 /* Hit the mount with full damage */
670 flag |= PROJECT_PLAYER;
676 * The spell is not well aimed, so
677 * partly affect both player and
685 if (affect_monster(caster_ptr, who, effective_dist, y, x, dam, typ, flag, see_s_msg))
689 /* Player affected one monster (without "jumping") */
690 if (!who && (project_m_n == 1) && !jump) {
693 if (caster_ptr->current_floor_ptr->grid_array[y][x].m_idx > 0) {
694 monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[caster_ptr->current_floor_ptr->grid_array[y][x].m_idx];
697 if (!caster_ptr->image)
698 monster_race_track(caster_ptr, m_ptr->ap_r_idx);
699 health_track(caster_ptr, caster_ptr->current_floor_ptr->grid_array[y][x].m_idx);
705 if (flag & (PROJECT_KILL)) {
707 for (int i = 0; i < grids; i++) {
709 if (gm[dist + 1] == i)
714 if (!player_bold(caster_ptr, y, x))
717 /* Find the closest point in the blast */
719 effective_dist = dist_to_line(y, x, y1, x1, by, bx);
721 effective_dist = dist;
724 if (caster_ptr->riding) {
725 if (flag & PROJECT_PLAYER) {
726 /* Hit the player with full damage */
730 * Hack -- When this grid was not the
731 * original target, a beam or bolt
732 * would hit either player or mount,
733 * and should be choosen randomly.
735 * But already choosen to hit the
736 * mount at this point.
738 * Or aimed on your horse.
740 else if (flag & (PROJECT_BEAM | PROJECT_REFLECTABLE | PROJECT_AIMED)) {
742 * A beam or bolt is well aimed
744 * So don't affects the player.
749 * The spell is not well aimed,
750 * So partly affect the player too.
756 if (affect_player(who, caster_ptr, who_name, effective_dist, y, x, dam, typ, flag, monspell, project))
761 if (caster_ptr->riding) {
762 GAME_TEXT m_name[MAX_NLEN];
763 monster_desc(caster_ptr, m_name, &caster_ptr->current_floor_ptr->m_list[caster_ptr->riding], 0);
764 if (rakubadam_m > 0) {
765 if (process_fall_off_horse(caster_ptr, rakubadam_m, FALSE)) {
766 msg_format(_("%^sに振り落とされた!", "%^s has thrown you off!"), m_name);
770 if (caster_ptr->riding && rakubadam_p > 0) {
771 if (process_fall_off_horse(caster_ptr, rakubadam_p, FALSE)) {
772 msg_format(_("%^sから落ちてしまった!", "You have fallen from %s."), m_name);