OSDN Git Service

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