OSDN Git Service

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