OSDN Git Service

22dc2b8d056b43a3769a9f81835b94166f9ef736
[hengbandforosx/hengbandosx.git] / src / melee / monster-attack-monster.cpp
1 /*!
2  * @brief モンスター同士が乱闘する処理
3  * @date 2020/05/23
4  * @author Hourier
5  */
6
7 #include "melee/monster-attack-monster.h"
8 #include "combat/attack-accuracy.h"
9 #include "combat/hallucination-attacks-table.h"
10 #include "core/disturbance.h"
11 #include "core/player-redraw-types.h"
12 #include "dungeon/dungeon-flag-types.h"
13 #include "effect/attribute-types.h"
14 #include "effect/effect-characteristics.h"
15 #include "effect/effect-processor.h"
16 #include "main/sound-definitions-table.h"
17 #include "main/sound-of-music.h"
18 #include "melee/melee-postprocess.h"
19 #include "melee/melee-switcher.h"
20 #include "melee/melee-util.h"
21 #include "monster-attack/monster-attack-effect.h"
22 #include "monster-race/monster-race-hook.h"
23 #include "monster-race/monster-race.h"
24 #include "monster-race/race-flags-resistance.h"
25 #include "monster-race/race-flags1.h"
26 #include "monster-race/race-flags2.h"
27 #include "monster-race/race-flags3.h"
28 #include "monster/monster-describer.h"
29 #include "monster/monster-info.h"
30 #include "monster/monster-status-setter.h"
31 #include "monster/monster-status.h"
32 #include "spell-kind/spells-teleport.h"
33 #include "spell-realm/spells-hex.h"
34 #include "system/dungeon-info.h"
35 #include "system/floor-type-definition.h"
36 #include "system/monster-entity.h"
37 #include "system/monster-race-info.h"
38 #include "system/player-type-definition.h"
39 #include "system/redrawing-flags-updater.h"
40 #include "util/string-processor.h"
41 #include "view/display-messages.h"
42
43 static void heal_monster_by_melee(PlayerType *player_ptr, mam_type *mam_ptr)
44 {
45     if (!monster_living(mam_ptr->t_ptr->r_idx) || (mam_ptr->damage <= 2)) {
46         return;
47     }
48
49     bool did_heal = mam_ptr->m_ptr->hp < mam_ptr->m_ptr->maxhp;
50     mam_ptr->m_ptr->hp += damroll(4, mam_ptr->damage / 6);
51     if (mam_ptr->m_ptr->hp > mam_ptr->m_ptr->maxhp) {
52         mam_ptr->m_ptr->hp = mam_ptr->m_ptr->maxhp;
53     }
54
55     auto &rfu = RedrawingFlagsUpdater::get_instance();
56     if (player_ptr->health_who == mam_ptr->m_idx) {
57         rfu.set_flag(MainWindowRedrawingFlag::HEALTH);
58     }
59
60     if (player_ptr->riding == mam_ptr->m_idx) {
61         rfu.set_flag(MainWindowRedrawingFlag::UHEALTH);
62     }
63
64     if (mam_ptr->see_m && did_heal) {
65         msg_format(_("%sは体力を回復したようだ。", "%s^ appears healthier."), mam_ptr->m_name);
66     }
67 }
68
69 static void process_blow_effect(PlayerType *player_ptr, mam_type *mam_ptr)
70 {
71     auto *r_ptr = &monraces_info[mam_ptr->m_ptr->r_idx];
72     switch (mam_ptr->attribute) {
73     case BlowEffectType::FEAR:
74         project(player_ptr, mam_ptr->m_idx, 0, mam_ptr->t_ptr->fy, mam_ptr->t_ptr->fx, mam_ptr->damage,
75             AttributeType::TURN_ALL, PROJECT_KILL | PROJECT_STOP | PROJECT_AIMED);
76         break;
77     case BlowEffectType::SLEEP:
78         project(player_ptr, mam_ptr->m_idx, 0, mam_ptr->t_ptr->fy, mam_ptr->t_ptr->fx, r_ptr->level,
79             AttributeType::OLD_SLEEP, PROJECT_KILL | PROJECT_STOP | PROJECT_AIMED);
80         break;
81     case BlowEffectType::HEAL:
82         heal_monster_by_melee(player_ptr, mam_ptr);
83         break;
84     default:
85         break;
86     }
87 }
88
89 static void aura_fire_by_melee(PlayerType *player_ptr, mam_type *mam_ptr)
90 {
91     auto *r_ptr = &monraces_info[mam_ptr->m_ptr->r_idx];
92     MonsterRaceInfo *tr_ptr = &monraces_info[mam_ptr->t_ptr->r_idx];
93     if (tr_ptr->aura_flags.has_not(MonsterAuraType::FIRE) || !MonsterRace(mam_ptr->m_ptr->r_idx).is_valid()) {
94         return;
95     }
96
97     if (r_ptr->resistance_flags.has_any_of(RFR_EFF_IM_FIRE_MASK) && is_original_ap_and_seen(player_ptr, mam_ptr->m_ptr)) {
98         r_ptr->r_resistance_flags.set(r_ptr->resistance_flags & RFR_EFF_IM_FIRE_MASK);
99         return;
100     }
101
102     if (mam_ptr->see_either) {
103         msg_format(_("%s^は突然熱くなった!", "%s^ is suddenly very hot!"), mam_ptr->m_name);
104     }
105
106     if (mam_ptr->m_ptr->ml && is_original_ap_and_seen(player_ptr, mam_ptr->t_ptr)) {
107         tr_ptr->aura_flags.set(MonsterAuraType::FIRE);
108     }
109
110     const auto dam = damroll(1 + ((tr_ptr->level) / 26), 1 + ((tr_ptr->level) / 17));
111     constexpr auto flags = PROJECT_KILL | PROJECT_STOP | PROJECT_AIMED;
112     project(player_ptr, mam_ptr->t_idx, 0, mam_ptr->m_ptr->fy, mam_ptr->m_ptr->fx, dam, AttributeType::FIRE, flags);
113 }
114
115 static void aura_cold_by_melee(PlayerType *player_ptr, mam_type *mam_ptr)
116 {
117     const auto *m_ptr = mam_ptr->m_ptr;
118     auto *r_ptr = &monraces_info[m_ptr->r_idx];
119     MonsterRaceInfo *tr_ptr = &monraces_info[mam_ptr->t_ptr->r_idx];
120     if (tr_ptr->aura_flags.has_not(MonsterAuraType::COLD) || !MonsterRace(m_ptr->r_idx).is_valid()) {
121         return;
122     }
123
124     if (r_ptr->resistance_flags.has_any_of(RFR_EFF_IM_COLD_MASK) && is_original_ap_and_seen(player_ptr, m_ptr)) {
125         r_ptr->r_resistance_flags.set(r_ptr->resistance_flags & RFR_EFF_IM_COLD_MASK);
126         return;
127     }
128
129     if (mam_ptr->see_either) {
130         msg_format(_("%s^は突然寒くなった!", "%s^ is suddenly very cold!"), mam_ptr->m_name);
131     }
132
133     if (m_ptr->ml && is_original_ap_and_seen(player_ptr, mam_ptr->t_ptr)) {
134         tr_ptr->aura_flags.set(MonsterAuraType::COLD);
135     }
136
137     const auto dam = damroll(1 + ((tr_ptr->level) / 26), 1 + ((tr_ptr->level) / 17));
138     constexpr auto flags = PROJECT_KILL | PROJECT_STOP | PROJECT_AIMED;
139     project(player_ptr, mam_ptr->t_idx, 0, m_ptr->fy, m_ptr->fx, dam, AttributeType::COLD, flags);
140 }
141
142 static void aura_elec_by_melee(PlayerType *player_ptr, mam_type *mam_ptr)
143 {
144     const auto *m_ptr = mam_ptr->m_ptr;
145     auto *r_ptr = &monraces_info[m_ptr->r_idx];
146     MonsterRaceInfo *tr_ptr = &monraces_info[mam_ptr->t_ptr->r_idx];
147     if (tr_ptr->aura_flags.has_not(MonsterAuraType::ELEC) || !MonsterRace(m_ptr->r_idx).is_valid()) {
148         return;
149     }
150
151     if (r_ptr->resistance_flags.has_any_of(RFR_EFF_IM_ELEC_MASK) && is_original_ap_and_seen(player_ptr, m_ptr)) {
152         r_ptr->r_resistance_flags.set(r_ptr->resistance_flags & RFR_EFF_IM_ELEC_MASK);
153         return;
154     }
155
156     if (mam_ptr->see_either) {
157         msg_format(_("%s^は電撃を食らった!", "%s^ gets zapped!"), mam_ptr->m_name);
158     }
159
160     if (m_ptr->ml && is_original_ap_and_seen(player_ptr, mam_ptr->t_ptr)) {
161         tr_ptr->aura_flags.set(MonsterAuraType::ELEC);
162     }
163
164     const auto dam = damroll(1 + ((tr_ptr->level) / 26), 1 + ((tr_ptr->level) / 17));
165     constexpr auto flags = PROJECT_KILL | PROJECT_STOP | PROJECT_AIMED;
166     project(player_ptr, mam_ptr->t_idx, 0, m_ptr->fy, m_ptr->fx, dam, AttributeType::ELEC, flags);
167 }
168
169 static bool check_same_monster(PlayerType *player_ptr, mam_type *mam_ptr)
170 {
171     if (mam_ptr->m_idx == mam_ptr->t_idx) {
172         return false;
173     }
174
175     auto *r_ptr = &monraces_info[mam_ptr->m_ptr->r_idx];
176     if (r_ptr->behavior_flags.has(MonsterBehaviorType::NEVER_BLOW)) {
177         return false;
178     }
179
180     if (dungeons_info[player_ptr->dungeon_idx].flags.has(DungeonFeatureType::NO_MELEE)) {
181         return false;
182     }
183
184     return true;
185 }
186
187 static void redraw_health_bar(PlayerType *player_ptr, mam_type *mam_ptr)
188 {
189     if (!mam_ptr->t_ptr->ml) {
190         return;
191     }
192
193     auto &rfu = RedrawingFlagsUpdater::get_instance();
194     if (player_ptr->health_who == mam_ptr->t_idx) {
195         rfu.set_flag(MainWindowRedrawingFlag::HEALTH);
196     }
197
198     if (player_ptr->riding == mam_ptr->t_idx) {
199         rfu.set_flag(MainWindowRedrawingFlag::UHEALTH);
200     }
201 }
202
203 static void describe_silly_melee(mam_type *mam_ptr)
204 {
205     char temp[MAX_NLEN];
206     if ((mam_ptr->act == nullptr) || !mam_ptr->see_either) {
207         return;
208     }
209
210 #ifdef JP
211     if (mam_ptr->do_silly_attack) {
212         mam_ptr->act = rand_choice(silly_attacks2);
213     }
214
215     strnfmt(temp, sizeof(temp), mam_ptr->act, mam_ptr->t_name);
216     msg_format("%s^は%s", mam_ptr->m_name, temp);
217 #else
218     if (mam_ptr->do_silly_attack) {
219         mam_ptr->act = rand_choice(silly_attacks);
220         strnfmt(temp, sizeof(temp), "%s %s.", mam_ptr->act, mam_ptr->t_name);
221     } else {
222         strnfmt(temp, sizeof(temp), mam_ptr->act, mam_ptr->t_name);
223     }
224
225     msg_format("%s^ %s", mam_ptr->m_name, temp);
226 #endif
227 }
228
229 static void process_monster_attack_effect(PlayerType *player_ptr, mam_type *mam_ptr)
230 {
231     if (mam_ptr->pt == AttributeType::NONE) {
232         return;
233     }
234
235     if (!mam_ptr->explode) {
236         project(player_ptr, mam_ptr->m_idx, 0, mam_ptr->t_ptr->fy, mam_ptr->t_ptr->fx, mam_ptr->damage, mam_ptr->pt,
237             PROJECT_KILL | PROJECT_STOP | PROJECT_AIMED);
238     }
239
240     process_blow_effect(player_ptr, mam_ptr);
241     if (!mam_ptr->touched) {
242         return;
243     }
244
245     aura_fire_by_melee(player_ptr, mam_ptr);
246     aura_cold_by_melee(player_ptr, mam_ptr);
247     aura_elec_by_melee(player_ptr, mam_ptr);
248 }
249
250 static void process_melee(PlayerType *player_ptr, mam_type *mam_ptr)
251 {
252     const auto remaining_stun = mam_ptr->m_ptr->get_remaining_stun();
253     if (mam_ptr->effect != RaceBlowEffectType::NONE && !check_hit_from_monster_to_monster(mam_ptr->power, mam_ptr->rlev, mam_ptr->ac, remaining_stun)) {
254         describe_monster_missed_monster(player_ptr, mam_ptr);
255         return;
256     }
257
258     (void)set_monster_csleep(player_ptr, mam_ptr->t_idx, 0);
259     redraw_health_bar(player_ptr, mam_ptr);
260     describe_melee_method(player_ptr, mam_ptr);
261     describe_silly_melee(mam_ptr);
262     mam_ptr->obvious = true;
263     mam_ptr->damage = damroll(mam_ptr->d_dice, mam_ptr->d_side);
264     mam_ptr->attribute = BlowEffectType::NONE;
265     mam_ptr->pt = AttributeType::MONSTER_MELEE;
266     decide_monster_attack_effect(player_ptr, mam_ptr);
267     process_monster_attack_effect(player_ptr, mam_ptr);
268 }
269
270 static void thief_runaway_by_melee(PlayerType *player_ptr, mam_type *mam_ptr)
271 {
272     if (SpellHex(player_ptr).check_hex_barrier(mam_ptr->m_idx, HEX_ANTI_TELE)) {
273         if (mam_ptr->see_m) {
274             msg_print(_("泥棒は笑って逃げ...ようとしたがバリアに防がれた。", "The thief flees laughing...? But a magic barrier obstructs it."));
275         } else if (mam_ptr->known) {
276             player_ptr->current_floor_ptr->monster_noise = true;
277         }
278     } else {
279         if (mam_ptr->see_m) {
280             msg_print(_("泥棒は笑って逃げた!", "The thief flees laughing!"));
281         } else if (mam_ptr->known) {
282             player_ptr->current_floor_ptr->monster_noise = true;
283         }
284
285         teleport_away(player_ptr, mam_ptr->m_idx, MAX_PLAYER_SIGHT * 2 + 5, TELEPORT_SPONTANEOUS);
286     }
287 }
288
289 static void explode_monster_by_melee(PlayerType *player_ptr, mam_type *mam_ptr)
290 {
291     if (!mam_ptr->explode) {
292         return;
293     }
294
295     sound(SOUND_EXPLODE);
296     (void)set_monster_invulner(player_ptr, mam_ptr->m_idx, 0, false);
297     mon_take_hit_mon(player_ptr, mam_ptr->m_idx, mam_ptr->m_ptr->hp + 1, &mam_ptr->dead, &mam_ptr->fear,
298         _("は爆発して粉々になった。", " explodes into tiny shreds."), mam_ptr->m_idx);
299     mam_ptr->blinked = false;
300 }
301
302 /*!
303  * @brief MonsterRaceDefinitionで定義した攻撃回数の分だけ、モンスターからモンスターへの直接攻撃処理を繰り返す
304  * @param player_ptr プレイヤーへの参照ポインタ
305  * @param mam_ptr モンスター乱闘構造体への参照ポインタ
306  */
307 void repeat_melee(PlayerType *player_ptr, mam_type *mam_ptr)
308 {
309     const auto *m_ptr = mam_ptr->m_ptr;
310     auto *r_ptr = &monraces_info[m_ptr->r_idx];
311     for (int ap_cnt = 0; ap_cnt < MAX_NUM_BLOWS; ap_cnt++) {
312         mam_ptr->effect = r_ptr->blow[ap_cnt].effect;
313         mam_ptr->method = r_ptr->blow[ap_cnt].method;
314         mam_ptr->d_dice = r_ptr->blow[ap_cnt].d_dice;
315         mam_ptr->d_side = r_ptr->blow[ap_cnt].d_side;
316
317         if (!m_ptr->is_valid()) {
318             break;
319         }
320
321         const auto x_saver = mam_ptr->t_ptr->fx != mam_ptr->x_saver;
322         const auto y_saver = mam_ptr->t_ptr->fy != mam_ptr->y_saver;
323         if (x_saver || y_saver || mam_ptr->method == RaceBlowMethodType::NONE) {
324             break;
325         }
326
327         if (mam_ptr->method == RaceBlowMethodType::SHOOT) {
328             continue;
329         }
330
331         mam_ptr->power = mbe_info[enum2i(mam_ptr->effect)].power;
332         process_melee(player_ptr, mam_ptr);
333         if (!is_original_ap_and_seen(player_ptr, mam_ptr->m_ptr) || mam_ptr->do_silly_attack) {
334             continue;
335         }
336
337         if (!mam_ptr->obvious && !mam_ptr->damage && (r_ptr->r_blows[ap_cnt] <= 10)) {
338             continue;
339         }
340
341         if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) {
342             r_ptr->r_blows[ap_cnt]++;
343         }
344     }
345 }
346
347 /*!
348  * @brief モンスターから敵モンスターへの打撃攻撃処理
349  * @param m_idx 攻撃側モンスターの参照ID
350  * @param t_idx 目標側モンスターの参照ID
351  * @return 実際に打撃処理が行われた場合TRUEを返す
352  */
353 bool monst_attack_monst(PlayerType *player_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx)
354 {
355     mam_type tmp_mam;
356     mam_type *mam_ptr = initialize_mam_type(player_ptr, &tmp_mam, m_idx, t_idx);
357
358     if (!check_same_monster(player_ptr, mam_ptr)) {
359         return false;
360     }
361
362     angband_strcpy(mam_ptr->m_name, monster_desc(player_ptr, mam_ptr->m_ptr, 0).data(), sizeof(mam_ptr->m_name));
363     angband_strcpy(mam_ptr->t_name, monster_desc(player_ptr, mam_ptr->t_ptr, 0).data(), sizeof(mam_ptr->t_name));
364     if (!mam_ptr->see_either && mam_ptr->known) {
365         player_ptr->current_floor_ptr->monster_noise = true;
366     }
367
368     if (player_ptr->riding && (m_idx == player_ptr->riding)) {
369         disturb(player_ptr, true, true);
370     }
371
372     repeat_melee(player_ptr, mam_ptr);
373     explode_monster_by_melee(player_ptr, mam_ptr);
374     if (!mam_ptr->blinked || !MonsterRace(mam_ptr->m_ptr->r_idx).is_valid()) {
375         return true;
376     }
377
378     thief_runaway_by_melee(player_ptr, mam_ptr);
379     return true;
380 }