OSDN Git Service

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