OSDN Git Service

6e7e623bee78b46a7508d7c9ffa851617667f98e
[hengband/hengband.git] / src / effect / effect-player.c
1 /*!
2  * todo 単体で1000行を超えている。要分割
3  * @brief 魔法によるプレーヤーへの効果まとめ
4  * @date 2020/04/29
5  * @author Hourier
6  */
7
8 #include "angband.h"
9 #include "effect/effect-player-util.h"
10 #include "effect/effect-player.h"
11 #include "effect/effect-player-switcher.h"
12 #include "main/sound-definitions-table.h"
13 #include "realm/realm-hex.h"
14 #include "effect/spells-effect-util.h"
15 #include "player-move.h"
16 #include "spell/spells-type.h"
17 #include "spell/spells2.h"
18 #include "player-effects.h"
19 #include "effect/effect-characteristics.h"
20
21 typedef enum effect_player_check_result
22 {
23         EP_CHECK_FALSE = 0,
24         EP_CHECK_TRUE = 1,
25         EP_CHECK_CONTINUE = 2,
26 } ep_check_result;
27
28 /*!
29  * @brief effect_player_type構造体を初期化する
30  * @param ep_ptr 初期化前の構造体
31  * @param who 魔法を唱えたモンスター (0ならプレーヤー自身)
32  * @param dam 基本威力
33  * @param effect_type 効果属性
34  * @param flag 効果フラグ
35  * @param monspell 効果元のモンスター魔法ID
36  * @return 初期化後の構造体ポインタ
37  */
38 static effect_player_type *initialize_effect_player(effect_player_type *ep_ptr, MONSTER_IDX who, HIT_POINT dam, EFFECT_ID effect_type, BIT_FLAGS flag, int monspell)
39 {
40         ep_ptr->rlev = 0;
41         ep_ptr->m_ptr = NULL;
42         ep_ptr->get_damage = 0;
43         ep_ptr->who = who;
44         ep_ptr->dam = dam;
45         ep_ptr->effect_type = effect_type;
46         ep_ptr->flag = flag;
47         ep_ptr->monspell = monspell;
48         return ep_ptr;
49 }
50
51
52 /*!
53  * @brief ボルト魔法を反射する
54  * @param target_ptr プレーヤーへの参照ポインタ
55  * @param ep_ptr プレーヤー効果構造体への参照ポインタ
56  * @return 当たったらFALSE、反射したらTRUE
57  */
58 static bool process_bolt_reflection(player_type *target_ptr,
59                                     effect_player_type *ep_ptr, project_func project) {
60         bool can_bolt_hit = target_ptr->reflect || (((target_ptr->special_defense & KATA_FUUJIN) != 0) && !target_ptr->blind);
61         can_bolt_hit &= (ep_ptr->flag & PROJECT_REFLECTABLE) != 0;
62         can_bolt_hit &= !one_in_(10);
63         if (!can_bolt_hit) return FALSE;
64
65         POSITION t_y, t_x;
66         int max_attempts = 10;
67         sound(SOUND_REFLECT);
68
69         if (target_ptr->blind)
70                 msg_print(_("何かが跳ね返った!", "Something bounces!"));
71         else if (target_ptr->special_defense & KATA_FUUJIN)
72                 msg_print(_("風の如く武器を振るって弾き返した!", "The attack bounces!"));
73         else
74                 msg_print(_("攻撃が跳ね返った!", "The attack bounces!"));
75
76         if (ep_ptr->who > 0)
77         {
78                 floor_type *floor_ptr = target_ptr->current_floor_ptr;
79                 monster_type m_type = floor_ptr->m_list[ep_ptr->who];
80                 do
81                 {
82                         t_y = m_type.fy - 1 + randint1(3);
83                         t_x = m_type.fx - 1 + randint1(3);
84                         max_attempts--;
85                 } while (max_attempts && in_bounds2u(floor_ptr, t_y, t_x) && !projectable(target_ptr, target_ptr->y, target_ptr->x, t_y, t_x));
86
87                 if (max_attempts < 1)
88                 {
89                         t_y = m_type.fy;
90                         t_x = m_type.fx;
91                 }
92         }
93         else
94         {
95                 t_y = target_ptr->y - 1 + randint1(3);
96                 t_x = target_ptr->x - 1 + randint1(3);
97         }
98
99         (*project)(target_ptr, 0, 0, t_y, t_x, ep_ptr->dam, ep_ptr->effect_type, (PROJECT_STOP | PROJECT_KILL | PROJECT_REFLECTABLE), ep_ptr->monspell);
100         disturb(target_ptr, TRUE, TRUE);
101         return TRUE;
102 }
103
104
105 /*!
106  * @brief 反射・忍者の変わり身などでそもそも当たらない状況を判定する
107  * @param target_ptr プレーヤーへの参照ポインタ
108  * @param ep_ptr プレーヤー効果構造体への参照ポインタ
109  * @param y 目標Y座標
110  * @param x 目標X座標
111  * @return 当たらなかったらFALSE、反射したらTRUE、当たったらCONTINUE
112  */
113 static ep_check_result check_continue_player_effect(player_type *target_ptr,
114                                                     effect_player_type *ep_ptr,
115                                                     POSITION y, POSITION x,
116                                                     project_func project) {
117         if (!player_bold(target_ptr, y, x))
118                 return EP_CHECK_FALSE;
119
120         if (((target_ptr->special_defense & NINJA_KAWARIMI) != 0) &&
121                 (ep_ptr->dam > 0) &&
122                 (randint0(55) < (target_ptr->lev * 3 / 5 + 20)) &&
123                 (ep_ptr->who > 0) &&
124                 (ep_ptr->who != target_ptr->riding) &&
125                 kawarimi(target_ptr, TRUE))
126                 return EP_CHECK_FALSE;
127
128         if ((ep_ptr->who == 0) || (ep_ptr->who == target_ptr->riding))
129                 return EP_CHECK_FALSE;
130
131         if (process_bolt_reflection(target_ptr, ep_ptr, project))
132                 return EP_CHECK_TRUE;
133
134         return EP_CHECK_CONTINUE;
135 }
136
137
138 /*!
139  * @brief 魔法を発したモンスター名を記述する
140  * @param target_ptr プレーヤーへの参照ポインタ
141  * @param ep_ptr プレーヤー効果構造体への参照ポインタ
142  * @param who_name モンスター名
143  * @return なし
144  */
145 static void describe_effect_source(player_type *target_ptr, effect_player_type *ep_ptr, concptr who_name)
146 {
147         if (ep_ptr->who > 0)
148         {
149                 ep_ptr->m_ptr = &target_ptr->current_floor_ptr->m_list[ep_ptr->who];
150                 ep_ptr->rlev = (&r_info[ep_ptr->m_ptr->r_idx])->level >= 1 ? (&r_info[ep_ptr->m_ptr->r_idx])->level : 1;
151                 monster_desc(target_ptr, ep_ptr->m_name, ep_ptr->m_ptr, 0);
152                 strcpy(ep_ptr->killer, who_name);
153                 return;
154         }
155
156         switch (ep_ptr->who)
157         {
158         case PROJECT_WHO_UNCTRL_POWER:
159                 strcpy(ep_ptr->killer, _("制御できない力の氾流", "uncontrollable power storm"));
160                 break;
161         case PROJECT_WHO_GLASS_SHARDS:
162                 strcpy(ep_ptr->killer, _("ガラスの破片", "shards of glass"));
163                 break;
164         default:
165                 strcpy(ep_ptr->killer, _("罠", "a trap"));
166                 break;
167         }
168
169         strcpy(ep_ptr->m_name, ep_ptr->killer);
170 }
171
172
173 /*!
174  * @brief 汎用的なビーム/ボルト/ボール系によるプレイヤーへの効果処理 / Helper function for "project()" below.
175  * @param who 魔法を発動したモンスター(0ならばプレイヤー、負値ならば自然発生) / Index of "source" monster (zero for "player")
176  * @param who_name 効果を起こしたモンスターの名前
177  * @param r 効果半径(ビーム/ボルト = 0 / ボール = 1以上) / Radius of explosion (0 = beam/bolt, 1 to 9 = ball)
178  * @param y 目標Y座標 / Target y location (or location to travel "towards")
179  * @param x 目標X座標 / Target x location (or location to travel "towards")
180  * @param dam 基本威力 / Base damage roll to apply to affected monsters (or player)
181  * @param effect_type 効果属性 / Type of damage to apply to monsters (and objects)
182  * @param flag 効果フラグ
183  * @param monspell 効果元のモンスター魔法ID
184  * @return 何か一つでも効力があればTRUEを返す / TRUE if any "effects" of the projection were observed, else FALSE
185  */
186 bool affect_player(MONSTER_IDX who, player_type *target_ptr, concptr who_name,
187                    int r, POSITION y, POSITION x, HIT_POINT dam,
188                    EFFECT_ID effect_type, BIT_FLAGS flag, int monspell,
189                    project_func project) {
190         effect_player_type tmp_effect;
191         effect_player_type *ep_ptr = initialize_effect_player(&tmp_effect, who, dam, effect_type, flag, monspell);
192         ep_check_result check_result = check_continue_player_effect(target_ptr, ep_ptr, y, x, project);
193         if (check_result != EP_CHECK_CONTINUE) return check_result;
194
195         if (ep_ptr->dam > 1600) ep_ptr->dam = 1600;
196
197         ep_ptr->dam = (ep_ptr->dam + r) / (r + 1);
198         describe_effect_source(target_ptr, ep_ptr, who_name);
199         switch_effects_player(target_ptr, ep_ptr);
200
201         revenge_store(target_ptr, ep_ptr->get_damage);
202         if ((target_ptr->tim_eyeeye || hex_spelling(target_ptr, HEX_EYE_FOR_EYE))
203                 && (ep_ptr->get_damage > 0) && !target_ptr->is_dead && (ep_ptr->who > 0))
204         {
205                 GAME_TEXT m_name_self[80];
206                 monster_desc(target_ptr, m_name_self, ep_ptr->m_ptr, MD_PRON_VISIBLE | MD_POSSESSIVE | MD_OBJECTIVE);
207                 msg_format(_("攻撃が%s自身を傷つけた!", "The attack of %s has wounded %s!"), ep_ptr->m_name, m_name_self);
208                 (*project)(target_ptr, 0, 0, ep_ptr->m_ptr->fy, ep_ptr->m_ptr->fx, ep_ptr->get_damage, GF_MISSILE, PROJECT_KILL, -1);
209                 if (target_ptr->tim_eyeeye) set_tim_eyeeye(target_ptr, target_ptr->tim_eyeeye - 5, TRUE);
210         }
211
212         if (target_ptr->riding && ep_ptr->dam > 0)
213         {
214                 rakubadam_p = (ep_ptr->dam > 200) ? 200 : ep_ptr->dam;
215         }
216
217         disturb(target_ptr, TRUE, TRUE);
218         if ((target_ptr->special_defense & NINJA_KAWARIMI) && ep_ptr->dam && ep_ptr->who && (ep_ptr->who != target_ptr->riding))
219         {
220                 (void)kawarimi(target_ptr, FALSE);
221         }
222
223         return TRUE;
224 }