OSDN Git Service

Merge pull request #3721 from daradarach/feature/nodequest
[hengbandforosx/hengbandosx.git] / src / spell-realm / spells-hex.cpp
1 #include "spell-realm/spells-hex.h"
2 #include "core/asking-player.h"
3 #include "core/window-redrawer.h"
4 #include "effect/effect-characteristics.h"
5 #include "effect/effect-processor.h"
6 #include "monster-attack/monster-attack-player.h"
7 #include "monster-race/monster-race.h"
8 #include "player-base/player-class.h"
9 #include "player-info/spell-hex-data-type.h"
10 #include "player/attack-defense-types.h"
11 #include "player/player-skill.h"
12 #include "realm/realm-hex-numbers.h"
13 #include "spell-kind/spells-teleport.h"
14 #include "spell-realm/spells-crusade.h"
15 #include "spell-realm/spells-song.h"
16 #include "spell/spell-info.h"
17 #include "spell/spells-execution.h"
18 #include "spell/technic-info-table.h"
19 #include "status/action-setter.h"
20 #include "system/angband-exceptions.h"
21 #include "system/floor-type-definition.h"
22 #include "system/monster-entity.h"
23 #include "system/monster-race-info.h"
24 #include "system/player-type-definition.h"
25 #include "system/redrawing-flags-updater.h"
26 #include "term/screen-processor.h"
27 #include "util/bit-flags-calculator.h"
28 #include "util/int-char-converter.h"
29 #include "view/display-messages.h"
30 #ifdef JP
31 #else
32 #include "monster/monster-describer.h"
33 #include "monster/monster-description-types.h"
34 #endif
35
36 /*!< 呪術の最大詠唱数 */
37 constexpr int MAX_KEEP = 4;
38
39 SpellHex::SpellHex(PlayerType *player_ptr)
40     : player_ptr(player_ptr)
41     , spell_hex_data(PlayerClass(player_ptr).get_specific_data<spell_hex_data_type>())
42 {
43     if (!this->spell_hex_data) {
44         return;
45     }
46
47     HexSpellFlagGroup::get_flags(this->spell_hex_data->casting_spells, std::back_inserter(this->casting_spells));
48
49     if (this->casting_spells.size() > MAX_KEEP) {
50         THROW_EXCEPTION(std::logic_error, "Invalid numbers of hex magics keep!");
51     }
52 }
53
54 SpellHex::SpellHex(PlayerType *player_ptr, MonsterAttackPlayer *monap_ptr)
55     : player_ptr(player_ptr)
56     , monap_ptr(monap_ptr)
57 {
58 }
59
60 /*!
61  * @brief プレイヤーが詠唱中の全呪術を停止する
62  */
63 void SpellHex::stop_all_spells()
64 {
65     for (auto spell : this->casting_spells) {
66         exe_spell(this->player_ptr, REALM_HEX, spell, SpellProcessType::STOP);
67     }
68
69     this->spell_hex_data->casting_spells.clear();
70     if (this->player_ptr->action == ACTION_SPELL) {
71         set_action(this->player_ptr, ACTION_NONE);
72     }
73
74     auto &rfu = RedrawingFlagsUpdater::get_instance();
75     static constexpr auto flags_srf = {
76         StatusRecalculatingFlag::BONUS,
77         StatusRecalculatingFlag::HP,
78         StatusRecalculatingFlag::MP,
79         StatusRecalculatingFlag::SPELLS,
80     };
81     rfu.set_flags(flags_srf);
82     static constexpr auto flags_mwrf = {
83         MainWindowRedrawingFlag::EXTRA,
84         MainWindowRedrawingFlag::HP,
85         MainWindowRedrawingFlag::MP,
86     };
87     rfu.set_flags(flags_mwrf);
88 }
89
90 /*!
91  * @brief プレイヤーが詠唱中の呪術から選択式で一つまたは全てを停止する
92  * @return 停止したらtrue、停止をキャンセルしたらfalse
93  */
94 bool SpellHex::stop_spells_with_selection()
95 {
96     if (!this->is_spelling_any()) {
97         msg_print(_("呪文を詠唱していません。", "You are not casting a spell."));
98         return false;
99     }
100
101     auto casting_num = this->get_casting_num();
102     if ((casting_num == 1) || (this->player_ptr->lev < 35)) {
103         this->stop_all_spells();
104         return true;
105     }
106
107     constexpr auto fmt = _("どの呪文の詠唱を中断しますか?(呪文 %c-%c, 'l'全て, ESC)", "Which spell do you stop casting? (Spell %c-%c, 'l' to all, ESC)");
108     const auto prompt = format(fmt, I2A(0), I2A(casting_num - 1));
109     screen_save();
110     const auto &[is_all, choice] = select_spell_stopping(prompt);
111     if (is_all) {
112         return true;
113     }
114
115     screen_load();
116     if (choice) {
117         auto n = this->casting_spells[A2I(*choice)];
118         exe_spell(this->player_ptr, REALM_HEX, n, SpellProcessType::STOP);
119         this->reset_casting_flag(i2enum<spell_hex_type>(n));
120     }
121
122     auto &rfu = RedrawingFlagsUpdater::get_instance();
123     static constexpr auto flags_srf = {
124         StatusRecalculatingFlag::BONUS,
125         StatusRecalculatingFlag::HP,
126         StatusRecalculatingFlag::MP,
127         StatusRecalculatingFlag::SPELLS,
128     };
129     rfu.set_flags(flags_srf);
130     static constexpr auto flags_mwrf = {
131         MainWindowRedrawingFlag::EXTRA,
132         MainWindowRedrawingFlag::HP,
133         MainWindowRedrawingFlag::MP,
134     };
135     rfu.set_flags(flags_mwrf);
136     return choice.has_value();
137 }
138
139 /*!
140  * @brief 中断する呪術を選択する
141  * @param out_val 呪文名
142  * @return
143  * Item1: 全ての呪文を中断するならばtrue、1つの呪文を中断するならばfalse
144  * Item2: 選択が完了したらtrue、キャンセルならばfalse
145  * Item3: 選択した呪文番号 (a~d、lの5択)
146  */
147 std::pair<bool, std::optional<char>> SpellHex::select_spell_stopping(std::string_view prompt)
148 {
149     while (true) {
150         this->display_casting_spells_list();
151         const auto choice_opt = input_command(prompt, true);
152         if (!choice_opt) {
153             return { false, std::nullopt };
154         }
155
156         auto choice = choice_opt.value();
157         if (isupper(choice)) {
158             choice = static_cast<char>(tolower(choice));
159         }
160
161         if (choice == 'l') {
162             screen_load();
163             this->stop_all_spells();
164             return { true, choice };
165         }
166
167         if ((choice < I2A(0)) || (choice > I2A(this->get_casting_num() - 1))) {
168             continue;
169         }
170
171         return { false, choice };
172     }
173 }
174
175 void SpellHex::display_casting_spells_list()
176 {
177     constexpr auto y = 1;
178     constexpr auto x = 20;
179     auto n = 0;
180     term_erase(x, y);
181     prt(_("     名前", "     Name"), y, x + 5);
182     for (auto spell : this->casting_spells) {
183         term_erase(x, y + n + 1);
184         const auto spell_name = exe_spell(this->player_ptr, REALM_HEX, spell, SpellProcessType::NAME);
185         put_str(format("%c)  %s", I2A(n), spell_name->data()), y + n + 1, x + 2);
186         n++;
187     }
188 }
189
190 /*!
191  * @brief 一定時間毎に呪術で消費するMPを処理する
192  */
193 void SpellHex::decrease_mana()
194 {
195     if (!this->spell_hex_data) {
196         return;
197     }
198
199     if (this->spell_hex_data->casting_spells.none() && this->spell_hex_data->interrupting_spells.none()) {
200         return;
201     }
202
203     auto need_restart = this->check_restart();
204     if (this->player_ptr->anti_magic) {
205         this->stop_all_spells();
206         return;
207     }
208
209     if (!this->process_mana_cost(need_restart)) {
210         return;
211     }
212
213     this->gain_exp();
214     for (auto spell : this->casting_spells) {
215         exe_spell(this->player_ptr, REALM_HEX, spell, SpellProcessType::CONTNUATION);
216     }
217 }
218
219 /*!
220  * @brief 継続的な呪文の詠唱が可能な程度にMPが残っているか確認し、残量に応じて継続・中断を行う
221  * @param need_restart 詠唱を再開するか否か
222  * @return MPが足りているか否か
223  * @todo 64ビットの割り算をしなければいけない箇所には見えない. 調査の後不要ならば消すこと.
224  */
225 bool SpellHex::process_mana_cost(const bool need_restart)
226 {
227     auto need_mana = this->calc_need_mana();
228     uint need_mana_frac = 0;
229     s64b_div(&need_mana, &need_mana_frac, 0, 3); /* Divide by 3 */
230     need_mana += this->get_casting_num() - 1;
231
232     auto enough_mana = s64b_cmp(this->player_ptr->csp, this->player_ptr->csp_frac, need_mana, need_mana_frac) >= 0;
233     if (!enough_mana) {
234         this->stop_all_spells();
235         return false;
236     }
237
238     s64b_sub(&(this->player_ptr->csp), &(this->player_ptr->csp_frac), need_mana, need_mana_frac);
239     auto &rfu = RedrawingFlagsUpdater::get_instance();
240     rfu.set_flag(MainWindowRedrawingFlag::MP);
241     if (!need_restart) {
242         return true;
243     }
244
245     msg_print(_("詠唱を再開した。", "You restart casting."));
246     this->player_ptr->action = ACTION_SPELL;
247     static constexpr auto flags_srf = {
248         StatusRecalculatingFlag::BONUS,
249         StatusRecalculatingFlag::HP,
250         StatusRecalculatingFlag::MONSTER_STATUSES,
251     };
252     rfu.set_flags(flags_srf);
253     static constexpr auto flags_mwrf = {
254         MainWindowRedrawingFlag::MAP,
255         MainWindowRedrawingFlag::TIMED_EFFECT,
256         MainWindowRedrawingFlag::ACTION,
257     };
258     rfu.set_flags(flags_mwrf);
259     static constexpr auto flags_swrf = {
260         SubWindowRedrawingFlag::OVERHEAD,
261         SubWindowRedrawingFlag::DUNGEON,
262     };
263     rfu.set_flags(flags_swrf);
264     return true;
265 }
266
267 bool SpellHex::check_restart()
268 {
269     if (this->spell_hex_data->interrupting_spells.none()) {
270         return false;
271     }
272
273     this->spell_hex_data->casting_spells = this->spell_hex_data->interrupting_spells;
274     this->spell_hex_data->interrupting_spells.clear();
275     return true;
276 }
277
278 int SpellHex::calc_need_mana()
279 {
280     auto need_mana = 0;
281     for (auto spell : this->casting_spells) {
282         const auto *s_ptr = &technic_info[REALM_HEX - MIN_TECHNIC][spell];
283         need_mana += mod_need_mana(this->player_ptr, s_ptr->smana, spell, REALM_HEX);
284     }
285
286     return need_mana;
287 }
288
289 void SpellHex::gain_exp()
290 {
291     PlayerSkill ps(player_ptr);
292     for (auto spell : this->casting_spells) {
293         if (!this->is_spelling_specific(spell)) {
294             continue;
295         }
296
297         ps.gain_continuous_spell_skill_exp(REALM_HEX, spell);
298     }
299 }
300
301 /*!
302  * @brief プレイヤーの呪術詠唱枠がすでに最大かどうかを返す
303  * @return すでに全枠を利用しているならTRUEを返す
304  */
305 bool SpellHex::is_casting_full_capacity() const
306 {
307     auto k_max = (this->player_ptr->lev / 15) + 1;
308     k_max = std::min(k_max, MAX_KEEP);
309     return this->get_casting_num() >= k_max;
310 }
311
312 /*!
313  * @brief 一定ゲームターン毎に復讐処理の残り期間の判定を行う
314  */
315 void SpellHex::continue_revenge()
316 {
317     if (!this->spell_hex_data || (this->get_revenge_turn() == 0)) {
318         return;
319     }
320
321     switch (this->get_revenge_type()) {
322     case SpellHexRevengeType::PATIENCE:
323         exe_spell(this->player_ptr, REALM_HEX, HEX_PATIENCE, SpellProcessType::CONTNUATION);
324         return;
325     case SpellHexRevengeType::REVENGE:
326         exe_spell(this->player_ptr, REALM_HEX, HEX_REVENGE, SpellProcessType::CONTNUATION);
327         return;
328     default:
329         return;
330     }
331 }
332
333 /*!
334  * @brief 復讐ダメージの追加を行う
335  * @param dam 蓄積されるダメージ量
336  */
337 void SpellHex::store_vengeful_damage(int dam)
338 {
339     if (!this->spell_hex_data || (this->get_revenge_turn() == 0)) {
340         return;
341     }
342
343     this->set_revenge_power(dam, false);
344 }
345
346 /*!
347  * @brief 呪術結界の判定
348  * @param m_idx 判定の対象となるモンスターID
349  * @return 呪術の効果が適用されるならTRUEを返す
350  * @details v3.0.0現在は反テレポート・反魔法・反増殖の3種類
351  */
352 bool SpellHex::check_hex_barrier(MONSTER_IDX m_idx, spell_hex_type type) const
353 {
354     const auto *m_ptr = &this->player_ptr->current_floor_ptr->m_list[m_idx];
355     const auto *r_ptr = &m_ptr->get_monrace();
356     return this->is_spelling_specific(type) && ((this->player_ptr->lev * 3 / 2) >= randint1(r_ptr->level));
357 }
358
359 bool SpellHex::is_spelling_specific(int hex) const
360 {
361     return this->spell_hex_data && this->spell_hex_data->casting_spells.has(i2enum<spell_hex_type>(hex));
362 }
363
364 bool SpellHex::is_spelling_any() const
365 {
366     return this->spell_hex_data && (this->get_casting_num() > 0);
367 }
368
369 void SpellHex::interrupt_spelling()
370 {
371     this->spell_hex_data->interrupting_spells = this->spell_hex_data->casting_spells;
372     this->spell_hex_data->casting_spells.clear();
373 }
374
375 /*!
376  * @brief 呪術「目には目を」の効果処理
377  * @param this->player_ptr プレイヤーへの参照ポインタ
378  * @param monap_ptr モンスターからプレイヤーへの直接攻撃構造体への参照ポインタ
379  */
380 void SpellHex::eyes_on_eyes()
381 {
382     if (this->monap_ptr == nullptr) {
383         THROW_EXCEPTION(std::logic_error, "Invalid constructor was used!");
384     }
385
386     const auto is_eyeeye_finished = (this->player_ptr->tim_eyeeye == 0) && !this->is_spelling_specific(HEX_EYE_FOR_EYE);
387     if (is_eyeeye_finished || (this->monap_ptr->get_damage == 0) || this->player_ptr->is_dead) {
388         return;
389     }
390
391 #ifdef JP
392     msg_format("攻撃が%s自身を傷つけた!", this->monap_ptr->m_name);
393 #else
394     const auto m_name_self = monster_desc(this->player_ptr, this->monap_ptr->m_ptr, MD_PRON_VISIBLE | MD_POSSESSIVE | MD_OBJECTIVE);
395     msg_format("The attack of %s has wounded %s!", this->monap_ptr->m_name, m_name_self.data());
396 #endif
397     const auto y = this->monap_ptr->m_ptr->fy;
398     const auto x = this->monap_ptr->m_ptr->fx;
399     project(this->player_ptr, 0, 0, y, x, this->monap_ptr->get_damage, AttributeType::MISSILE, PROJECT_KILL);
400     if (this->player_ptr->tim_eyeeye) {
401         set_tim_eyeeye(this->player_ptr, this->player_ptr->tim_eyeeye - 5, true);
402     }
403 }
404
405 void SpellHex::thief_teleport()
406 {
407     if (this->monap_ptr == nullptr) {
408         THROW_EXCEPTION(std::logic_error, "Invalid constructor was used!");
409     }
410
411     if (!this->monap_ptr->blinked || !this->monap_ptr->alive || this->player_ptr->is_dead) {
412         return;
413     }
414
415     if (this->check_hex_barrier(this->monap_ptr->m_idx, HEX_ANTI_TELE)) {
416         msg_print(_("泥棒は笑って逃げ...ようとしたがバリアに防がれた。", "The thief flees laughing...? But a magic barrier obstructs it."));
417     } else {
418         msg_print(_("泥棒は笑って逃げた!", "The thief flees laughing!"));
419         teleport_away(this->player_ptr, this->monap_ptr->m_idx, MAX_PLAYER_SIGHT * 2 + 5, TELEPORT_SPONTANEOUS);
420     }
421 }
422
423 void SpellHex::set_casting_flag(spell_hex_type type)
424 {
425     this->spell_hex_data->casting_spells.set(type);
426 }
427
428 void SpellHex::reset_casting_flag(spell_hex_type type)
429 {
430     this->spell_hex_data->casting_spells.reset(type);
431 }
432
433 int32_t SpellHex::get_casting_num() const
434 {
435     return this->spell_hex_data->casting_spells.count();
436 }
437
438 int32_t SpellHex::get_revenge_power() const
439 {
440     return this->spell_hex_data->revenge_power;
441 }
442
443 void SpellHex::set_revenge_power(int32_t power, bool substitution)
444 {
445     if (substitution) {
446         this->spell_hex_data->revenge_power = power;
447     } else {
448         this->spell_hex_data->revenge_power += power;
449     }
450 }
451
452 byte SpellHex::get_revenge_turn() const
453 {
454     return this->spell_hex_data->revenge_turn;
455 }
456
457 /*!
458  * @brief 復讐の残りターンをセットするか、残りターン数を減らす
459  * @param turn 残りターン (非負整数であること)
460  * @param substitution セットならtrue、ターン減少ならfalse
461  */
462 void SpellHex::set_revenge_turn(byte turn, bool substitution)
463 {
464     if (substitution) {
465         this->spell_hex_data->revenge_turn = turn;
466     } else {
467         this->spell_hex_data->revenge_turn -= turn;
468     }
469 }
470
471 SpellHexRevengeType SpellHex::get_revenge_type() const
472 {
473     return this->spell_hex_data->revenge_type;
474 }
475
476 void SpellHex::set_revenge_type(SpellHexRevengeType type)
477 {
478     this->spell_hex_data->revenge_type = type;
479 }