OSDN Git Service

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