OSDN Git Service

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