OSDN Git Service

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