OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / player-attack / attack-chaos-effect.cpp
1 /*!
2  * @brief 特殊属性武器で攻撃した際の追加効果処理
3  * @date 2020/05/23
4  * @author Hourier
5  * @details
6  * カオス属性、魔術属性、ゴールデンハンマー
7  */
8
9 #include "player-attack/attack-chaos-effect.h"
10 #include "artifact/fixed-art-types.h"
11 #include "flavor/flavor-describer.h"
12 #include "flavor/object-flavor-types.h"
13 #include "inventory/inventory-object.h"
14 #include "inventory/inventory-slot-types.h"
15 #include "lore/lore-store.h"
16 #include "monster-race//race-ability-mask.h"
17 #include "monster-race/monster-race.h"
18 #include "monster-race/race-flags-resistance.h"
19 #include "monster-race/race-flags1.h"
20 #include "monster-race/race-flags3.h"
21 #include "monster-race/race-resistance-mask.h"
22 #include "monster/monster-describer.h"
23 #include "monster/monster-info.h"
24 #include "monster/monster-status-setter.h"
25 #include "monster/monster-status.h"
26 #include "object/object-mark-types.h"
27 #include "player-attack/player-attack.h"
28 #include "player/attack-defense-types.h"
29 #include "realm/realm-hex-numbers.h"
30 #include "spell-kind/spells-polymorph.h"
31 #include "spell-kind/spells-sight.h"
32 #include "spell-kind/spells-teleport.h"
33 #include "spell-realm/spells-hex.h"
34 #include "system/floor-type-definition.h"
35 #include "system/item-entity.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/bit-flags-calculator.h"
41 #include "util/string-processor.h"
42 #include "view/display-messages.h"
43
44 /*!
45  * @brief 打撃でモンスターを混乱させる処理
46  * @param player_ptr プレイヤーへの参照ポインタ
47  * @param pa_ptr 直接攻撃構造体への参照ポインタ
48  * @param can_resist レベルで抵抗可能ならTRUE、できないならFALSE
49  * @details
50  * カオス属性や混乱の手
51  */
52 static void attack_confuse(PlayerType *player_ptr, player_attack_type *pa_ptr, bool can_resist = true)
53 {
54     if (player_ptr->special_attack & ATTACK_CONFUSE) {
55         player_ptr->special_attack &= ~(ATTACK_CONFUSE);
56         msg_print(_("手の輝きがなくなった。", "Your hands stop glowing."));
57         RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::TIMED_EFFECT);
58     }
59
60     auto *r_ptr = pa_ptr->r_ptr;
61     if (r_ptr->flags3 & RF3_NO_CONF) {
62         if (is_original_ap_and_seen(player_ptr, pa_ptr->m_ptr)) {
63             r_ptr->r_flags3 |= RF3_NO_CONF;
64         }
65         msg_format(_("%s^には効果がなかった。", "%s^ is unaffected."), pa_ptr->m_name);
66
67     } else if (can_resist && randint0(100) < r_ptr->level) {
68         msg_format(_("%s^には効果がなかった。", "%s^ is unaffected."), pa_ptr->m_name);
69     } else {
70         msg_format(_("%s^は混乱したようだ。", "%s^ appears confused."), pa_ptr->m_name);
71         (void)set_monster_confused(player_ptr, pa_ptr->m_idx, pa_ptr->m_ptr->get_remaining_confusion() + 10 + randint0(player_ptr->lev) / 5);
72     }
73 }
74
75 /*!
76  * @brief 打撃でモンスターを朦朧とさせる処理
77  * @param player_ptr プレイヤーへの参照ポインタ
78  * @param pa_ptr 直接攻撃構造体への参照ポインタ
79  * @param can_resist レベルで抵抗可能ならTRUE、できないならFALSE
80  * @details
81  * 魔術属性
82  */
83 static void attack_stun(PlayerType *player_ptr, player_attack_type *pa_ptr, bool can_resist = true)
84 {
85     auto *r_ptr = pa_ptr->r_ptr;
86     if (any_bits(r_ptr->flags3, RF3_NO_STUN)) {
87         if (is_original_ap_and_seen(player_ptr, pa_ptr->m_ptr)) {
88             set_bits(r_ptr->flags3, RF3_NO_STUN);
89         }
90         msg_format(_("%s^には効果がなかった。", "%s^ is unaffected."), pa_ptr->m_name);
91     } else if (can_resist && randint0(100) < r_ptr->level) {
92         msg_format(_("%s^には効果がなかった。", "%s^ is unaffected."), pa_ptr->m_name);
93     } else {
94         msg_format(_("%s^は朦朧としたようだ。", "%s^ appears stunned."), pa_ptr->m_name);
95         (void)set_monster_stunned(player_ptr, pa_ptr->m_idx, pa_ptr->m_ptr->get_remaining_stun() + 10 + randint0(player_ptr->lev) / 5);
96     }
97 }
98
99 /*!
100  * @brief 打撃でモンスターを恐怖させる処理
101  * @param player_ptr プレイヤーへの参照ポインタ
102  * @param pa_ptr 直接攻撃構造体への参照ポインタ
103  * @param can_resist レベルで抵抗可能ならTRUE、できないならFALSE
104  * @details
105  * 魔術属性
106  */
107 static void attack_scare(PlayerType *player_ptr, player_attack_type *pa_ptr, bool can_resist = true)
108 {
109     auto *r_ptr = pa_ptr->r_ptr;
110     if (any_bits(r_ptr->flags3, RF3_NO_FEAR)) {
111         if (is_original_ap_and_seen(player_ptr, pa_ptr->m_ptr)) {
112             set_bits(r_ptr->flags3, RF3_NO_FEAR);
113         }
114         msg_format(_("%s^には効果がなかった。", "%s^ is unaffected."), pa_ptr->m_name);
115     } else if (can_resist && randint0(100) < r_ptr->level) {
116         msg_format(_("%s^には効果がなかった。", "%s^ is unaffected."), pa_ptr->m_name);
117     } else {
118         msg_format(_("%s^は恐怖して逃げ出した!", "%s^ flees in terror!"), pa_ptr->m_name);
119         (void)set_monster_monfear(player_ptr, pa_ptr->m_idx, pa_ptr->m_ptr->get_remaining_fear() + 10 + randint0(player_ptr->lev) / 5);
120     }
121 }
122
123 /*!
124  * @brief 打撃でモンスターを無力化する処理
125  * @param player_ptr プレイヤーへの参照ポインタ
126  * @param pa_ptr 直接攻撃構造体への参照ポインタ
127  * @details
128  * 魔術属性
129  */
130 static void attack_dispel(PlayerType *player_ptr, player_attack_type *pa_ptr)
131 {
132     if (pa_ptr->r_ptr->ability_flags.has_none_of(RF_ABILITY_ATTACK_MASK) && pa_ptr->r_ptr->ability_flags.has_none_of(RF_ABILITY_INDIRECT_MASK)) {
133         return;
134     }
135
136     auto dd = 2;
137     if (pa_ptr->m_ptr->mtimed[MTIMED_SLOW]) {
138         dd += 1;
139     }
140     if (pa_ptr->m_ptr->mtimed[MTIMED_FAST]) {
141         dd += 2;
142     }
143     if (pa_ptr->m_ptr->mtimed[MTIMED_INVULNER]) {
144         dd += 3;
145     }
146
147     msg_print(_("武器が敵の魔力を吸い取った!", "The weapon drains mana from your enemy!"));
148     dispel_monster_status(player_ptr, pa_ptr->m_idx);
149
150     auto sp = damroll(dd, 8);
151     player_ptr->csp = std::min(player_ptr->msp, player_ptr->csp + sp);
152     RedrawingFlagsUpdater::get_instance().set_flag(MainWindowRedrawingFlag::MP);
153 }
154
155 /*!
156  * @brief 打撃でモンスターを調査する処理
157  * @param player_ptr プレイヤーへの参照ポインタ
158  * @param pa_ptr 直接攻撃構造体への参照ポインタ
159  * @details
160  * 魔術属性
161  */
162 static void attack_probe(PlayerType *player_ptr, player_attack_type *pa_ptr)
163 {
164     msg_print(_("刃が敵を調査した...", "The blade probed your enemy..."));
165     msg_print(nullptr);
166     msg_print(probed_monster_info(player_ptr, pa_ptr->m_ptr, pa_ptr->r_ptr));
167     msg_print(nullptr);
168     (void)lore_do_probe(player_ptr, pa_ptr->r_idx);
169 }
170
171 /*!
172  * @breif カオス武器でのテレポート・アウェイを行うか判定する (抵抗されたら無効)
173  * @param player_ptr プレイヤーへの参照ポインタ
174  * @param pa_ptr 直接攻撃構造体への参照ポインタ
175  * @return 抵抗されたらTRUE、アウェイされるならFALSE
176  */
177 static bool judge_tereprt_resistance(PlayerType *player_ptr, player_attack_type *pa_ptr)
178 {
179     auto *r_ptr = pa_ptr->r_ptr;
180     if (r_ptr->resistance_flags.has_not(MonsterResistanceType::RESIST_TELEPORT)) {
181         return false;
182     }
183
184     if (r_ptr->kind_flags.has(MonsterKindType::UNIQUE)) {
185         if (is_original_ap_and_seen(player_ptr, pa_ptr->m_ptr)) {
186             r_ptr->r_resistance_flags.set(MonsterResistanceType::RESIST_TELEPORT);
187         }
188
189         msg_format(_("%s^には効果がなかった。", "%s^ is unaffected!"), pa_ptr->m_name);
190         return true;
191     }
192
193     if (r_ptr->level > randint1(100)) {
194         if (is_original_ap_and_seen(player_ptr, pa_ptr->m_ptr)) {
195             r_ptr->r_resistance_flags.set(MonsterResistanceType::RESIST_TELEPORT);
196         }
197
198         msg_format(_("%s^は抵抗力を持っている!", "%s^ resists!"), pa_ptr->m_name);
199         return true;
200     }
201
202     return false;
203 }
204
205 /*!
206  * @brief カオス武器でのテレポート・アウェイを実行する
207  * @param player_ptr プレイヤーへの参照ポインタ
208  * @param pa_ptr 直接攻撃構造体への参照ポインタ
209  * @param num 現在の攻撃回数 (テレポートしてしまったら追加攻撃できないのでその補正)
210  */
211 static void attack_teleport_away(PlayerType *player_ptr, player_attack_type *pa_ptr, int *num)
212 {
213     if (judge_tereprt_resistance(player_ptr, pa_ptr)) {
214         return;
215     }
216
217     msg_format(_("%s^は消えた!", "%s^ disappears!"), pa_ptr->m_name);
218     teleport_away(player_ptr, pa_ptr->m_idx, 50, TELEPORT_PASSIVE);
219     *num = pa_ptr->num_blow + 1;
220     *(pa_ptr->mdeath) = true;
221 }
222
223 /*!
224  * @brief カオス武器でのテレポート・アウェイを実行する
225  * @param player_ptr プレイヤーへの参照ポインタ
226  * @param pa_ptr 直接攻撃構造体への参照ポインタ
227  * @param y モンスターのY座標
228  * @param x モンスターのX座標
229  */
230 static void attack_polymorph(PlayerType *player_ptr, player_attack_type *pa_ptr, POSITION y, POSITION x)
231 {
232     auto *r_ptr = pa_ptr->r_ptr;
233     if (r_ptr->kind_flags.has(MonsterKindType::UNIQUE) || any_bits(r_ptr->flags1, RF1_QUESTOR) || r_ptr->resistance_flags.has_any_of(RFR_EFF_RESIST_CHAOS_MASK)) {
234         return;
235     }
236
237     if (polymorph_monster(player_ptr, y, x)) {
238         msg_format(_("%s^は変化した!", "%s^ changes!"), pa_ptr->m_name);
239         *(pa_ptr->fear) = false;
240         pa_ptr->weak = false;
241     } else {
242         msg_format(_("%s^には効果がなかった。", "%s^ is unaffected."), pa_ptr->m_name);
243     }
244
245     pa_ptr->m_ptr = &player_ptr->current_floor_ptr->m_list[pa_ptr->m_idx];
246     angband_strcpy(pa_ptr->m_name, monster_desc(player_ptr, pa_ptr->m_ptr, 0), sizeof(pa_ptr->m_name));
247 }
248
249 /*!
250  * @brief ゴールデンハンマーによるアイテム奪取処理
251  * @param player_ptr プレイヤーへの参照ポインタ
252  * @param pa_ptr 直接攻撃構造体への参照ポインタ
253  */
254 static void attack_golden_hammer(PlayerType *player_ptr, player_attack_type *pa_ptr)
255 {
256     auto *floor_ptr = player_ptr->current_floor_ptr;
257     auto *m_ptr = &floor_ptr->m_list[pa_ptr->m_idx];
258     if (m_ptr->hold_o_idx_list.empty()) {
259         return;
260     }
261
262     auto *q_ptr = &floor_ptr->o_list[m_ptr->hold_o_idx_list.front()];
263     const auto item_name = describe_flavor(player_ptr, q_ptr, OD_NAME_ONLY);
264     q_ptr->held_m_idx = 0;
265     q_ptr->marked.clear().set(OmType::TOUCHED);
266     m_ptr->hold_o_idx_list.pop_front();
267     msg_format(_("%sを奪った。", "You snatched %s."), item_name.data());
268     store_item_to_inventory(player_ptr, q_ptr);
269 }
270
271 /*!
272  * @brief カオス武器その他でモンスターのステータスを変化させる
273  * @param player_ptr プレイヤーへの参照ポインタ
274  * @param pa_ptr 直接攻撃構造体への参照ポインタ
275  * @param y モンスターのY座標
276  * @param x モンスターのX座標
277  * @param num 現在の攻撃回数
278  */
279 void change_monster_stat(PlayerType *player_ptr, player_attack_type *pa_ptr, const POSITION y, const POSITION x, int *num)
280 {
281     auto should_confuse = any_bits(player_ptr->special_attack, ATTACK_CONFUSE);
282     should_confuse |= pa_ptr->chaos_effect == CE_CONFUSION;
283     should_confuse |= pa_ptr->mode == HISSATSU_CONF;
284     should_confuse |= SpellHex(player_ptr).is_spelling_specific(HEX_CONFUSION);
285     if (should_confuse) {
286         attack_confuse(player_ptr, pa_ptr);
287     }
288
289     if (pa_ptr->magical_effect == MagicalBrandEffectType::STUN) {
290         attack_stun(player_ptr, pa_ptr, false);
291     }
292
293     if (pa_ptr->magical_effect == MagicalBrandEffectType::SCARE) {
294         attack_scare(player_ptr, pa_ptr, false);
295     }
296
297     if (pa_ptr->magical_effect == MagicalBrandEffectType::DISPELL) {
298         attack_dispel(player_ptr, pa_ptr);
299     }
300
301     if (pa_ptr->magical_effect == MagicalBrandEffectType::PROBE) {
302         attack_probe(player_ptr, pa_ptr);
303     }
304
305     if (pa_ptr->chaos_effect == CE_TELE_AWAY) {
306         attack_teleport_away(player_ptr, pa_ptr, num);
307     }
308
309     if (pa_ptr->chaos_effect == CE_POLYMORPH && (randint1(90) > monraces_info[pa_ptr->m_ptr->r_idx].level)) {
310         attack_polymorph(player_ptr, pa_ptr, y, x);
311     }
312
313     const auto &item = player_ptr->inventory_list[enum2i(INVEN_MAIN_HAND) + pa_ptr->hand];
314     if (item.is_specific_artifact(FixedArtifactId::G_HAMMER)) {
315         attack_golden_hammer(player_ptr, pa_ptr);
316     }
317 }