OSDN Git Service

[Refactor] #2666 PlayerType::phase_out をAngbandSystem に移した
[hengbandforosx/hengbandosx.git] / src / melee / melee-postprocess.cpp
1 /*!
2  * @brief モンスター同士の打撃後処理 / Melee post-process.
3  * @date 2014/01/17
4  * @author
5  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke\n
6  * This software may be copied and distributed for educational, research,\n
7  * and not for profit purposes provided that this copyright and statement\n
8  * are included in all such copies.  Other copyrights may also apply.\n
9  * 2014 Deskull rearranged comment for Doxygen.\n
10  * @details
11  */
12
13 #include "melee/melee-postprocess.h"
14 #include "core/disturbance.h"
15 #include "effect/attribute-types.h"
16 #include "floor/cave.h"
17 #include "floor/geometry.h"
18 #include "grid/grid.h"
19 #include "main/sound-definitions-table.h"
20 #include "main/sound-of-music.h"
21 #include "monster-attack/monster-attack-table.h"
22 #include "monster-floor/monster-death.h"
23 #include "monster-floor/monster-move.h"
24 #include "monster-floor/monster-remover.h"
25 #include "monster-race/monster-race-hook.h"
26 #include "monster-race/monster-race.h"
27 #include "monster-race/race-flags-resistance.h"
28 #include "monster-race/race-flags1.h"
29 #include "monster-race/race-flags3.h"
30 #include "monster-race/race-flags7.h"
31 #include "monster/monster-describer.h"
32 #include "monster/monster-description-types.h"
33 #include "monster/monster-info.h"
34 #include "monster/monster-status-setter.h"
35 #include "monster/monster-status.h"
36 #include "pet/pet-fall-off.h"
37 #include "player-info/class-info.h"
38 #include "player-info/race-types.h"
39 #include "player/player-personality-types.h"
40 #include "system/angband-system.h"
41 #include "system/floor-type-definition.h"
42 #include "system/monster-entity.h"
43 #include "system/monster-race-info.h"
44 #include "system/player-type-definition.h"
45 #include "system/redrawing-flags-updater.h"
46 #include "util/bit-flags-calculator.h"
47 #include "util/string-processor.h"
48 #include "view/display-messages.h"
49 #include <string>
50
51 // Melee-post-process-type
52 struct mam_pp_type {
53     mam_pp_type(PlayerType *player_ptr, MONSTER_IDX m_idx, int dam, bool *dead, bool *fear, std::string_view note, MONSTER_IDX who);
54     MONSTER_IDX m_idx;
55     MonsterEntity *m_ptr;
56     int dam;
57     bool *dead;
58     bool *fear;
59     std::string note;
60     MONSTER_IDX who;
61     bool seen;
62     bool known; /* Can the player be aware of this attack? */
63     std::string m_name;
64 };
65
66 mam_pp_type::mam_pp_type(PlayerType *player_ptr, MONSTER_IDX m_idx, int dam, bool *dead, bool *fear, std::string_view note, MONSTER_IDX who)
67     : m_idx(m_idx)
68     , m_ptr(&player_ptr->current_floor_ptr->m_list[m_idx])
69     , dam(dam)
70     , dead(dead)
71     , fear(fear)
72     , note(note)
73     , who(who)
74 {
75     this->seen = is_seen(player_ptr, this->m_ptr);
76     this->known = this->m_ptr->cdis <= MAX_PLAYER_SIGHT;
77     this->m_name = monster_desc(player_ptr, m_ptr, 0);
78 }
79
80 static void prepare_redraw(PlayerType *player_ptr, mam_pp_type *mam_pp_ptr)
81 {
82     if (!mam_pp_ptr->m_ptr->ml) {
83         return;
84     }
85
86     auto &rfu = RedrawingFlagsUpdater::get_instance();
87     if (player_ptr->health_who == mam_pp_ptr->m_idx) {
88         rfu.set_flag(MainWindowRedrawingFlag::HEALTH);
89     }
90
91     if (player_ptr->riding == mam_pp_ptr->m_idx) {
92         rfu.set_flag(MainWindowRedrawingFlag::UHEALTH);
93     }
94 }
95
96 /*!
97  * @brief モンスターが無敵だった場合の処理
98  * @param mam_pp_ptr 標的モンスター構造体への参照ポインタ
99  * @return 無敵ノーダメならTRUE、無敵でないか無敵を貫通したらFALSE
100  */
101 static bool process_invulnerability(mam_pp_type *mam_pp_ptr)
102 {
103     if (mam_pp_ptr->m_ptr->is_invulnerable() && randint0(PENETRATE_INVULNERABILITY)) {
104         return false;
105     }
106
107     if (mam_pp_ptr->seen) {
108         msg_format(_("%s^はダメージを受けない。", "%s^ is unharmed."), mam_pp_ptr->m_name.data());
109     }
110
111     return true;
112 }
113
114 /*!
115  * @brief 魔法完全防御持ちの処理
116  * @param mam_pp_ptr 標的モンスター構造体への参照ポインタ
117  * @return ノーダメならTRUE、 そうでないならFALSE
118  */
119 static bool process_all_resistances(mam_pp_type *mam_pp_ptr)
120 {
121     auto *r_ptr = &mam_pp_ptr->m_ptr->get_monrace();
122     if (r_ptr->resistance_flags.has_not(MonsterResistanceType::RESIST_ALL)) {
123         return false;
124     }
125
126     if (mam_pp_ptr->dam > 0) {
127         mam_pp_ptr->dam /= 100;
128         if ((mam_pp_ptr->dam == 0) && one_in_(3)) {
129             mam_pp_ptr->dam = 1;
130         }
131     }
132
133     if (mam_pp_ptr->dam != 0) {
134         return false;
135     }
136
137     if (mam_pp_ptr->seen) {
138         msg_format(_("%s^はダメージを受けない。", "%s^ is unharmed."), mam_pp_ptr->m_name.data());
139     }
140
141     return true;
142 }
143
144 /*!
145  * @brief モンスター死亡時のメッセージ表示
146  * @param player_ptr プレイヤーへの参照ポインタ
147  * @param mam_pp_ptr 標的モンスター構造体への参照ポインタ
148  * @details
149  * 見えない位置で死んだら何も表示しない
150  * 爆発して粉々になった等ならその旨を、残りは生命か無生命かで分岐
151  */
152 static void print_monster_dead_by_monster(PlayerType *player_ptr, mam_pp_type *mam_pp_ptr)
153 {
154     if (!mam_pp_ptr->known) {
155         return;
156     }
157
158     mam_pp_ptr->m_name = monster_desc(player_ptr, mam_pp_ptr->m_ptr, MD_TRUE_NAME);
159     if (!mam_pp_ptr->seen) {
160         player_ptr->current_floor_ptr->monster_noise = true;
161         return;
162     }
163
164     if (!mam_pp_ptr->note.empty()) {
165         sound_type kill_sound = mam_pp_ptr->m_ptr->has_living_flag() ? SOUND_KILL : SOUND_N_KILL;
166         sound(kill_sound);
167         msg_format(_("%s^%s", "%s^%s"), mam_pp_ptr->m_name.data(), mam_pp_ptr->note.data());
168         return;
169     }
170
171     if (!mam_pp_ptr->m_ptr->has_living_flag()) {
172         sound(SOUND_N_KILL);
173         msg_format(_("%s^は破壊された。", "%s^ is destroyed."), mam_pp_ptr->m_name.data());
174         return;
175     }
176
177     sound(SOUND_KILL);
178     msg_format(_("%s^は殺された。", "%s^ is killed."), mam_pp_ptr->m_name.data());
179 }
180
181 /*!
182  * @brief ダメージを受けたモンスターのHPが0未満になった際の処理
183  * @param player_ptr プレイヤーへの参照ポインタ
184  * @param mam_pp_ptr 標的モンスター構造体への参照ポインタ
185  * @return 生きていたらTRUE、それ以外 (ユニークは@以外の攻撃では死なない)はFALSE
186  */
187 static bool check_monster_hp(PlayerType *player_ptr, mam_pp_type *mam_pp_ptr)
188 {
189     const auto &monrace = mam_pp_ptr->m_ptr->get_monrace();
190     if (mam_pp_ptr->m_ptr->hp < 0) {
191         return false;
192     }
193
194     auto is_like_unique = monrace.kind_flags.has(MonsterKindType::UNIQUE);
195     is_like_unique |= any_bits(monrace.flags1, RF1_QUESTOR);
196     is_like_unique |= monrace.population_flags.has(MonsterPopulationType::NAZGUL);
197     if (is_like_unique && !AngbandSystem::get_instance().is_watching()) {
198         mam_pp_ptr->m_ptr->hp = 1;
199         return false;
200     }
201
202     *(mam_pp_ptr->dead) = true;
203     print_monster_dead_by_monster(player_ptr, mam_pp_ptr);
204     monster_gain_exp(player_ptr, mam_pp_ptr->who, mam_pp_ptr->m_ptr->r_idx);
205     monster_death(player_ptr, mam_pp_ptr->m_idx, false, AttributeType::NONE);
206     delete_monster_idx(player_ptr, mam_pp_ptr->m_idx);
207     *(mam_pp_ptr->fear) = false;
208     return true;
209 }
210
211 /*!
212  * @brief 死亡等で恐慌状態をキャンセルする
213  * @param player_ptr プレイヤーへの参照ポインタ
214  * @param mam_pp_ptr 標的モンスター構造体への参照ポインタ
215  */
216 static void cancel_fear_by_pain(PlayerType *player_ptr, mam_pp_type *mam_pp_ptr)
217 {
218     const auto &m_ref = *mam_pp_ptr->m_ptr;
219     const auto dam = mam_pp_ptr->dam;
220     if (!m_ref.is_fearful() || (dam <= 0) || !set_monster_monfear(player_ptr, mam_pp_ptr->m_idx, m_ref.get_remaining_fear() - randint1(dam / 4))) {
221         return;
222     }
223
224     *(mam_pp_ptr->fear) = false;
225 }
226
227 /*!
228  * @biref HP残量などに応じてモンスターを恐慌状態にする
229  * @param player_ptr プレイヤーへの参照ポインタ
230  * @param mam_pp_ptr 標的モンスター構造体への参照ポインタ
231  */
232 static void make_monster_fear(PlayerType *player_ptr, mam_pp_type *mam_pp_ptr)
233 {
234     auto *r_ptr = &mam_pp_ptr->m_ptr->get_monrace();
235     if (mam_pp_ptr->m_ptr->is_fearful() || (r_ptr->resistance_flags.has_not(MonsterResistanceType::NO_FEAR))) {
236         return;
237     }
238
239     int percentage = (100L * mam_pp_ptr->m_ptr->hp) / mam_pp_ptr->m_ptr->maxhp;
240     bool can_make_fear = ((percentage <= 10) && (randint0(10) < percentage)) || ((mam_pp_ptr->dam >= mam_pp_ptr->m_ptr->hp) && (randint0(100) < 80));
241     if (!can_make_fear) {
242         return;
243     }
244
245     *(mam_pp_ptr->fear) = true;
246     (void)set_monster_monfear(
247         player_ptr, mam_pp_ptr->m_idx, (randint1(10) + (((mam_pp_ptr->dam >= mam_pp_ptr->m_ptr->hp) && (percentage > 7)) ? 20 : ((11 - percentage) * 5))));
248 }
249
250 /*!
251  * @brief モンスター同士の乱闘による落馬処理
252  * @param player_ptr プレイヤーへの参照ポインタ
253  * @param mam_pp_ptr 標的モンスター構造体への参照ポインタ
254  */
255 static void fall_off_horse_by_melee(PlayerType *player_ptr, mam_pp_type *mam_pp_ptr)
256 {
257     if (!player_ptr->riding || (player_ptr->riding != mam_pp_ptr->m_idx) || (mam_pp_ptr->dam <= 0)) {
258         return;
259     }
260
261     mam_pp_ptr->m_name = monster_desc(player_ptr, mam_pp_ptr->m_ptr, 0);
262     if (mam_pp_ptr->m_ptr->hp > mam_pp_ptr->m_ptr->maxhp / 3) {
263         mam_pp_ptr->dam = (mam_pp_ptr->dam + 1) / 2;
264     }
265
266     if (process_fall_off_horse(player_ptr, (mam_pp_ptr->dam > 200) ? 200 : mam_pp_ptr->dam, false)) {
267         msg_format(_("%s^に振り落とされた!", "You have been thrown off from %s!"), mam_pp_ptr->m_name.data());
268     }
269 }
270
271 /*!
272  * @brief モンスターが敵モンスターに行う打撃処理 /
273  * Hack, based on mon_take_hit... perhaps all monster attacks on other monsters should use this?
274  * @param m_idx 目標となるモンスターの参照ID
275  * @param dam ダメージ量
276  * @param dead 目標となったモンスターの死亡状態を返す参照ポインタ
277  * @param fear 目標となったモンスターの恐慌状態を返す参照ポインタ
278  * @param note 目標モンスターが死亡した場合の特別メッセージ(nullptrならば標準表示を行う)
279  * @param who 打撃を行ったモンスターの参照ID
280  * @todo 打撃が当たった時の後処理 (爆発持ちのモンスターを爆発させる等)なので、関数名を変更する必要あり
281  */
282 void mon_take_hit_mon(PlayerType *player_ptr, MONSTER_IDX m_idx, int dam, bool *dead, bool *fear, std::string_view note, MONSTER_IDX who)
283 {
284     auto *floor_ptr = player_ptr->current_floor_ptr;
285     auto *m_ptr = &floor_ptr->m_list[m_idx];
286     mam_pp_type tmp_mam_pp(player_ptr, m_idx, dam, dead, fear, note, who);
287     mam_pp_type *mam_pp_ptr = &tmp_mam_pp;
288     prepare_redraw(player_ptr, mam_pp_ptr);
289     (void)set_monster_csleep(player_ptr, m_idx, 0);
290
291     if (player_ptr->riding && (m_idx == player_ptr->riding)) {
292         disturb(player_ptr, true, true);
293     }
294
295     if (process_invulnerability(mam_pp_ptr) || process_all_resistances(mam_pp_ptr)) {
296         return;
297     }
298
299     m_ptr->hp -= dam;
300     if (check_monster_hp(player_ptr, mam_pp_ptr)) {
301         return;
302     }
303
304     *dead = false;
305     cancel_fear_by_pain(player_ptr, mam_pp_ptr);
306     make_monster_fear(player_ptr, mam_pp_ptr);
307     if ((dam > 0) && !m_ptr->is_pet() && !m_ptr->is_friendly() && (mam_pp_ptr->who != m_idx)) {
308         const auto &m_ref = floor_ptr->m_list[who];
309         if (m_ref.is_pet() && !player_bold(player_ptr, m_ptr->target_y, m_ptr->target_x)) {
310             set_target(m_ptr, m_ref.fy, m_ref.fx);
311         }
312     }
313
314     fall_off_horse_by_melee(player_ptr, mam_pp_ptr);
315 }