OSDN Git Service

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