OSDN Git Service

[Refactor] #1389 Changed monap_type from struct to class MonsterAttackPlayer
[hengbandforosx/hengbandosx.git] / src / monster-attack / monster-attack-switcher.cpp
1 /*!
2  * @brief モンスターからプレイヤーへの直接攻撃をその種別において振り分ける
3  * @date 2020/05/31
4  * @author Hourier
5  * @details 長い処理はインクルード先の別ファイルにて行っている
6  */
7
8 #include "monster-attack/monster-attack-switcher.h"
9 #include "inventory/inventory-slot-types.h"
10 #include "mind/drs-types.h"
11 #include "mind/mind-mirror-master.h"
12 #include "monster-attack/monster-attack-lose.h"
13 #include "monster-attack/monster-attack-status.h"
14 #include "monster-attack/monster-attack-util.h"
15 #include "monster-attack/monster-eating.h"
16 #include "monster/monster-status.h"
17 #include "monster/monster-update.h"
18 #include "player/player-damage.h"
19 #include "player/player-status-flags.h"
20 #include "player/player-status-resist.h"
21 #include "player/player-status.h"
22 #include "spell-kind/earthquake.h"
23 #include "spell-kind/spells-equipment.h"
24 #include "status/bad-status-setter.h"
25 #include "status/base-status.h"
26 #include "status/element-resistance.h"
27 #include "status/experience.h"
28 #include "system/monster-type-definition.h"
29 #include "system/object-type-definition.h"
30 #include "system/player-type-definition.h"
31 #include "view/display-messages.h"
32
33 /*!
34  * @brief 毒ダメージを計算する
35  * @param player_ptr プレイヤーへの参照ポインタ
36  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
37  * @details 減衰の計算式がpoisではなくnukeなのは仕様 (1/3では減衰が強すぎると判断したため)
38  */
39 static void calc_blow_poison(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
40 {
41     if (monap_ptr->explode)
42         return;
43
44     if (!(has_resist_pois(player_ptr) || is_oppose_pois(player_ptr)) && !check_multishadow(player_ptr)
45         && BadStatusSetter(player_ptr).mod_poison(randint1(monap_ptr->rlev) + 5))
46         monap_ptr->obvious = true;
47
48     monap_ptr->damage = monap_ptr->damage * calc_nuke_damage_rate(player_ptr) / 100;
49     monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
50     update_smart_learn(player_ptr, monap_ptr->m_idx, DRS_POIS);
51 }
52
53 /*!
54  * @brief 劣化ダメージを計算する (耐性があれば、(1d4 + 4) / 9になる)
55  * @param player_ptr プレイヤーへの参照ポインタ
56  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
57  */
58 static void calc_blow_disenchant(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
59 {
60     if (monap_ptr->explode)
61         return;
62
63     if (!has_resist_disen(player_ptr) && !check_multishadow(player_ptr) && apply_disenchant(player_ptr, 0)) {
64         update_creature(player_ptr);
65         monap_ptr->obvious = true;
66     }
67
68     if (has_resist_disen(player_ptr))
69         monap_ptr->damage = monap_ptr->damage * (randint1(4) + 4) / 9;
70
71     monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
72     update_smart_learn(player_ptr, monap_ptr->m_idx, DRS_DISEN);
73 }
74
75 /*!
76  * @brief 魔道具吸収ダメージを計算する (消費魔力減少、呪文失敗率減少、魔道具使用能力向上があればそれぞれ-7.5%)
77  * @param player_ptr プレイヤーへの参照ポインタ
78  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
79  * @detals 魔道具使用能力向上フラグがあれば、吸収対象のアイテムをスキャンされる回数が半分で済む
80  */
81 static void calc_blow_un_power(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
82 {
83     int damage_ratio = 1000;
84     if (has_dec_mana(player_ptr))
85         damage_ratio -= 75;
86
87     if (has_easy_spell(player_ptr))
88         damage_ratio -= 75;
89
90     bool is_magic_mastery = has_magic_mastery(player_ptr) != 0;
91     if (is_magic_mastery)
92         damage_ratio -= 75;
93
94     monap_ptr->damage = monap_ptr->damage * damage_ratio / 1000;
95     monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
96     if (player_ptr->is_dead || check_multishadow(player_ptr))
97         return;
98
99     int max_draining_item = is_magic_mastery ? 5 : 10;
100     for (int i = 0; i < max_draining_item; i++) {
101         INVENTORY_IDX i_idx = (INVENTORY_IDX)randint0(INVEN_PACK);
102         monap_ptr->o_ptr = &player_ptr->inventory_list[i_idx];
103         if (monap_ptr->o_ptr->k_idx == 0)
104             continue;
105
106         if (process_un_power(player_ptr, monap_ptr))
107             break;
108     }
109 }
110
111 /*!
112  * @brief 盲目ダメージを計算する (耐性があれば、(1d4 + 3) / 8になる)
113  * @param player_ptr プレイヤーへの参照ポインタ
114  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
115  */
116 static void calc_blow_blind(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
117 {
118     if (has_resist_blind(player_ptr))
119         monap_ptr->damage = monap_ptr->damage * (randint1(4) + 3) / 8;
120
121     monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
122     if (player_ptr->is_dead)
123         return;
124
125     process_blind_attack(player_ptr, monap_ptr);
126     update_smart_learn(player_ptr, monap_ptr->m_idx, DRS_BLIND);
127 }
128
129 /*!
130  * @brief 混乱ダメージを計算する (耐性があれば、(1d4 + 3) / 8になる)
131  * @param player_ptr プレイヤーへの参照ポインタ
132  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
133  */
134 static void calc_blow_confusion(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
135 {
136     if (monap_ptr->explode) {
137         return;
138     }
139
140     if (has_resist_conf(player_ptr)) {
141         monap_ptr->damage = monap_ptr->damage * (randint1(4) + 3) / 8;
142     }
143
144     monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
145     if (player_ptr->is_dead) {
146         return;
147     }
148
149     if (!has_resist_conf(player_ptr) && !check_multishadow(player_ptr) && BadStatusSetter(player_ptr).mod_confusion(3 + randint1(monap_ptr->rlev))) {
150         monap_ptr->obvious = true;
151     }
152
153     update_smart_learn(player_ptr, monap_ptr->m_idx, DRS_CONF);
154 }
155
156 /*!
157  * @brief 恐怖ダメージを計算する (耐性があれば、(1d4 + 3) / 8になる)
158  * @param player_ptr プレイヤーへの参照ポインタ
159  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
160  */
161 static void calc_blow_fear(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
162 {
163     if (has_resist_fear(player_ptr))
164         monap_ptr->damage = monap_ptr->damage * (randint1(4) + 3) / 8;
165
166     monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
167     if (player_ptr->is_dead)
168         return;
169
170     process_terrify_attack(player_ptr, monap_ptr);
171     update_smart_learn(player_ptr, monap_ptr->m_idx, DRS_FEAR);
172 }
173
174 /*!
175  * @brief 麻痺ダメージを計算する (耐性があれば、(1d4 + 3) / 8になる)
176  * @param player_ptr プレイヤーへの参照ポインタ
177  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
178  */
179 static void calc_blow_paralysis(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
180 {
181     if (has_free_act(player_ptr))
182         monap_ptr->damage = monap_ptr->damage * (randint1(4) + 3) / 8;
183
184     monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
185     if (player_ptr->is_dead)
186         return;
187
188     process_paralyze_attack(player_ptr, monap_ptr);
189     update_smart_learn(player_ptr, monap_ptr->m_idx, DRS_FREE);
190 }
191
192 /*!
193  * @brief 経験値吸収ダメージを計算する (経験値保持と地獄耐性があれば、それぞれ-7.5%)
194  * @param player_ptr プレイヤーへの参照ポインタ
195  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
196  */
197 static void calc_blow_drain_exp(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr, const int drain_value, const int hold_exp_prob)
198 {
199     int32_t d = damroll(drain_value, 6) + (player_ptr->exp / 100) * MON_DRAIN_LIFE;
200     monap_ptr->obvious = true;
201     int damage_ratio = 1000;
202     if (has_hold_exp(player_ptr))
203         damage_ratio -= 75;
204
205     if (has_resist_neth(player_ptr))
206         damage_ratio -= 75;
207
208     monap_ptr->damage = monap_ptr->damage * damage_ratio / 1000;
209     monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
210     if (player_ptr->is_dead || check_multishadow(player_ptr))
211         return;
212
213     (void)drain_exp(player_ptr, d, d / 10, hold_exp_prob);
214 }
215
216 /*!
217  * @brief 時間逆転ダメージを計算する (耐性があれば、(1d4 + 4) / 9になる)
218  * @param player_ptr プレイヤーへの参照ポインタ
219  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
220  */
221 static void calc_blow_time(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
222 {
223     if (monap_ptr->explode)
224         return;
225
226     process_monster_attack_time(player_ptr, monap_ptr);
227     if (has_resist_time(player_ptr))
228         monap_ptr->damage = monap_ptr->damage * (randint1(4) + 4) / 9;
229
230     monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
231 }
232
233 /*!
234  * @brief 生命力吸収ダメージを計算する (経験値維持があれば9/10になる)
235  * @param player_ptr プレイヤーへの参照ポインタ
236  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
237  */
238 static void calc_blow_drain_life(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
239 {
240     int32_t d = damroll(60, 6) + (player_ptr->exp / 100) * MON_DRAIN_LIFE;
241     monap_ptr->obvious = true;
242     if (player_ptr->hold_exp)
243         monap_ptr->damage = monap_ptr->damage * 9 / 10;
244
245     monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
246     if (player_ptr->is_dead || check_multishadow(player_ptr))
247         return;
248
249     bool resist_drain = check_drain_hp(player_ptr, d);
250     process_drain_life(player_ptr, monap_ptr, resist_drain);
251 }
252
253 /*!
254  * @brief MPダメージを計算する (消費魔力減少、呪文失敗率減少、魔道具使用能力向上があればそれぞれ-5%)
255  * @param player_ptr プレイヤーへの参照ポインタ
256  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
257  */
258 static void calc_blow_drain_mana(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
259 {
260     monap_ptr->obvious = true;
261     int damage_ratio = 100;
262     if (has_dec_mana(player_ptr))
263         damage_ratio -= 5;
264
265     if (has_easy_spell(player_ptr))
266         damage_ratio -= 5;
267
268     if (has_magic_mastery(player_ptr))
269         damage_ratio -= 5;
270
271     monap_ptr->damage = monap_ptr->damage * damage_ratio / 100;
272     process_drain_mana(player_ptr, monap_ptr);
273     update_smart_learn(player_ptr, monap_ptr->m_idx, DRS_MANA);
274 }
275
276 static void calc_blow_inertia(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
277 {
278     if ((player_ptr->fast > 0) || (player_ptr->pspeed >= 130))
279         monap_ptr->damage = monap_ptr->damage * (randint1(4) + 4) / 9;
280
281     monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
282     if (player_ptr->is_dead || check_multishadow(player_ptr))
283         return;
284
285     if (BadStatusSetter(player_ptr).mod_slowness(4 + randint0(monap_ptr->rlev / 10), false))
286         monap_ptr->obvious = true;
287 }
288
289 /*!
290 * @brief 空腹進行度を計算する (急速回復があれば+100%、遅消化があれば-50%)
291 */
292 static void calc_blow_hungry(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
293 {
294     if (player_ptr->regenerate)
295         monap_ptr->damage = monap_ptr->damage * 2;
296     if (player_ptr->slow_digest)
297         monap_ptr->damage = monap_ptr->damage / 2;
298
299     process_monster_attack_hungry(player_ptr, monap_ptr);
300 }
301
302 void switch_monster_blow_to_player(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
303 {
304     switch (monap_ptr->effect) {
305     case RaceBlowEffectType::NONE:
306         // ここには来ないはずだが、何らかのバグで来た場合はプレイヤーの不利益に
307         // ならないようダメージを 0 にしておく。
308         monap_ptr->damage = 0;
309         break;
310     case RaceBlowEffectType::SUPERHURT: { /* AC軽減あり / Player armor reduces total damage */
311         if (((randint1(monap_ptr->rlev * 2 + 300) > (monap_ptr->ac + 200)) || one_in_(13)) && !check_multishadow(player_ptr)) {
312             monap_ptr->damage -= (monap_ptr->damage * ((monap_ptr->ac < 150) ? monap_ptr->ac : 150) / 250);
313             msg_print(_("痛恨の一撃!", "It was a critical hit!"));
314             monap_ptr->damage = std::max(monap_ptr->damage, monap_ptr->damage * 2);
315             monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
316             break;
317         }
318     }
319         /* Fall through */
320     case RaceBlowEffectType::HURT: { /* AC軽減あり / Player armor reduces total damage */
321         monap_ptr->obvious = true;
322         monap_ptr->damage -= (monap_ptr->damage * ((monap_ptr->ac < 150) ? monap_ptr->ac : 150) / 250);
323         monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
324         break;
325     }
326     case RaceBlowEffectType::POISON:
327         calc_blow_poison(player_ptr, monap_ptr);
328         break;
329     case RaceBlowEffectType::UN_BONUS:
330         calc_blow_disenchant(player_ptr, monap_ptr);
331         break;
332     case RaceBlowEffectType::UN_POWER:
333         calc_blow_un_power(player_ptr, monap_ptr);
334         break;
335     case RaceBlowEffectType::EAT_GOLD:
336         monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
337         if (monster_confused_remaining(monap_ptr->m_ptr) || player_ptr->is_dead || check_multishadow(player_ptr))
338             break;
339
340         monap_ptr->obvious = true;
341         process_eat_gold(player_ptr, monap_ptr);
342         break;
343     case RaceBlowEffectType::EAT_ITEM: {
344         monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
345         if (!check_eat_item(player_ptr, monap_ptr))
346             break;
347
348         process_eat_item(player_ptr, monap_ptr);
349         break;
350     }
351
352     case RaceBlowEffectType::EAT_FOOD: {
353         monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
354         if (player_ptr->is_dead || check_multishadow(player_ptr))
355             break;
356
357         process_eat_food(player_ptr, monap_ptr);
358         break;
359     }
360     case RaceBlowEffectType::EAT_LITE: {
361         monap_ptr->o_ptr = &player_ptr->inventory_list[INVEN_LITE];
362         monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
363         if (player_ptr->is_dead || check_multishadow(player_ptr))
364             break;
365
366         process_eat_lite(player_ptr, monap_ptr);
367         break;
368     }
369     case RaceBlowEffectType::ACID: {
370         if (monap_ptr->explode)
371             break;
372
373         monap_ptr->obvious = true;
374         msg_print(_("酸を浴びせられた!", "You are covered in acid!"));
375         monap_ptr->get_damage += acid_dam(player_ptr, monap_ptr->damage, monap_ptr->ddesc, false);
376         update_creature(player_ptr);
377         update_smart_learn(player_ptr, monap_ptr->m_idx, DRS_ACID);
378         break;
379     }
380     case RaceBlowEffectType::ELEC: {
381         if (monap_ptr->explode)
382             break;
383
384         monap_ptr->obvious = true;
385         msg_print(_("電撃を浴びせられた!", "You are struck by electricity!"));
386         monap_ptr->get_damage += elec_dam(player_ptr, monap_ptr->damage, monap_ptr->ddesc, false);
387         update_smart_learn(player_ptr, monap_ptr->m_idx, DRS_ELEC);
388         break;
389     }
390     case RaceBlowEffectType::FIRE: {
391         if (monap_ptr->explode)
392             break;
393
394         monap_ptr->obvious = true;
395         msg_print(_("全身が炎に包まれた!", "You are enveloped in flames!"));
396         monap_ptr->get_damage += fire_dam(player_ptr, monap_ptr->damage, monap_ptr->ddesc, false);
397         update_smart_learn(player_ptr, monap_ptr->m_idx, DRS_FIRE);
398         break;
399     }
400     case RaceBlowEffectType::COLD: {
401         if (monap_ptr->explode)
402             break;
403
404         monap_ptr->obvious = true;
405         msg_print(_("全身が冷気で覆われた!", "You are covered with frost!"));
406         monap_ptr->get_damage += cold_dam(player_ptr, monap_ptr->damage, monap_ptr->ddesc, false);
407         update_smart_learn(player_ptr, monap_ptr->m_idx, DRS_COLD);
408         break;
409     }
410     case RaceBlowEffectType::BLIND:
411         calc_blow_blind(player_ptr, monap_ptr);
412         break;
413     case RaceBlowEffectType::CONFUSE:
414         calc_blow_confusion(player_ptr, monap_ptr);
415         break;
416     case RaceBlowEffectType::TERRIFY:
417         calc_blow_fear(player_ptr, monap_ptr);
418         break;
419     case RaceBlowEffectType::PARALYZE:
420         calc_blow_paralysis(player_ptr, monap_ptr);
421         break;
422     case RaceBlowEffectType::LOSE_STR:
423         calc_blow_lose_strength(player_ptr, monap_ptr);
424         break;
425     case RaceBlowEffectType::LOSE_INT:
426         calc_blow_lose_intelligence(player_ptr, monap_ptr);
427         break;
428     case RaceBlowEffectType::LOSE_WIS:
429         calc_blow_lose_wisdom(player_ptr, monap_ptr);
430         break;
431     case RaceBlowEffectType::LOSE_DEX:
432         calc_blow_lose_dexterity(player_ptr, monap_ptr);
433         break;
434     case RaceBlowEffectType::LOSE_CON:
435         calc_blow_lose_constitution(player_ptr, monap_ptr);
436         break;
437     case RaceBlowEffectType::LOSE_CHR:
438         calc_blow_lose_charisma(player_ptr, monap_ptr);
439         break;
440     case RaceBlowEffectType::LOSE_ALL:
441         calc_blow_lose_all(player_ptr, monap_ptr);
442         break;
443     case RaceBlowEffectType::SHATTER: { /* AC軽減あり / Player armor reduces total damage */
444         monap_ptr->obvious = true;
445         monap_ptr->damage -= (monap_ptr->damage * ((monap_ptr->ac < 150) ? monap_ptr->ac : 150) / 250);
446         monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
447         if (monap_ptr->damage > 23 || monap_ptr->explode)
448             earthquake(player_ptr, monap_ptr->m_ptr->fy, monap_ptr->m_ptr->fx, 8, monap_ptr->m_idx);
449
450         break;
451     }
452     case RaceBlowEffectType::EXP_10:
453         calc_blow_drain_exp(player_ptr, monap_ptr, 10, 95);
454         break;
455     case RaceBlowEffectType::EXP_20:
456         calc_blow_drain_exp(player_ptr, monap_ptr, 20, 90);
457         break;
458     case RaceBlowEffectType::EXP_40:
459         calc_blow_drain_exp(player_ptr, monap_ptr, 40, 75);
460         break;
461     case RaceBlowEffectType::EXP_80:
462         calc_blow_drain_exp(player_ptr, monap_ptr, 80, 50);
463         break;
464     case RaceBlowEffectType::DISEASE:
465         calc_blow_disease(player_ptr, monap_ptr);
466         break;
467     case RaceBlowEffectType::TIME:
468         calc_blow_time(player_ptr, monap_ptr);
469         break;
470     case RaceBlowEffectType::DR_LIFE:
471         calc_blow_drain_life(player_ptr, monap_ptr);
472         break;
473     case RaceBlowEffectType::DR_MANA:
474         calc_blow_drain_mana(player_ptr, monap_ptr);
475         break;
476     case RaceBlowEffectType::INERTIA:
477         calc_blow_inertia(player_ptr, monap_ptr);
478         break;
479     case RaceBlowEffectType::STUN:
480         monap_ptr->get_damage += take_hit(player_ptr, DAMAGE_ATTACK, monap_ptr->damage, monap_ptr->ddesc);
481         if (player_ptr->is_dead)
482             break;
483         process_stun_attack(player_ptr, monap_ptr);
484         break;
485     case RaceBlowEffectType::FLAVOR:
486         // フレーバー打撃は自明かつダメージ 0。
487         monap_ptr->obvious = true;
488         monap_ptr->damage = 0;
489         break;
490     case RaceBlowEffectType::HUNGRY:
491         calc_blow_hungry(player_ptr, monap_ptr);
492         break;
493
494     case RaceBlowEffectType::MAX:
495         break;
496     }
497 }