OSDN Git Service

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