OSDN Git Service

[Refactor] 魔法の熟練度処理を PlayerSkill クラスに移設
[hengbandforosx/hengbandosx.git] / src / spell / spell-info.cpp
1 #include "spell/spell-info.h"
2 #include "game-option/game-play-options.h"
3 #include "io/input-key-requester.h"
4 #include "monster-race/monster-race.h"
5 #include "player-info/class-info.h"
6 #include "player/player-skill.h"
7 #include "player/player-status-table.h"
8 #include "player/player-status.h"
9 #include "realm/realm-names-table.h"
10 #include "realm/realm-types.h"
11 #include "spell/spells-execution.h"
12 #include "system/floor-type-definition.h"
13 #include "system/monster-race-definition.h"
14 #include "system/monster-type-definition.h"
15 #include "system/player-type-definition.h"
16 #include "term/screen-processor.h"
17 #include "term/term-color-types.h"
18 #include "timed-effect/player-stun.h"
19 #include "timed-effect/timed-effects.h"
20 #include "util/bit-flags-calculator.h"
21 #include "util/int-char-converter.h"
22 #include "view/display-messages.h"
23
24 // 5%
25 static const int extra_min_magic_fail_rate = 2;
26
27 /*!
28  * @brief 呪文の消費MPを返す /
29  * Modify mana consumption rate using spell exp and dec_mana
30  * @param player_ptr プレイヤーへの参照ポインタ
31  * @param need_mana 基本消費MP
32  * @param spell 呪文ID
33  * @param realm 魔法領域
34  * @return 消費MP
35  */
36 MANA_POINT mod_need_mana(player_type *player_ptr, MANA_POINT need_mana, SPELL_IDX spell, int16_t realm)
37 {
38 #define MANA_CONST 2400
39 #define MANA_DIV 4
40 #define DEC_MANA_DIV 3
41     if ((realm > REALM_NONE) && (realm <= MAX_REALM)) {
42         need_mana = need_mana * (MANA_CONST + SPELL_EXP_EXPERT - PlayerSkill(player_ptr).exp_of_spell(realm, spell)) + (MANA_CONST - 1);
43         need_mana *= player_ptr->dec_mana ? DEC_MANA_DIV : MANA_DIV;
44         need_mana /= MANA_CONST * MANA_DIV;
45         if (need_mana < 1)
46             need_mana = 1;
47     } else {
48         if (player_ptr->dec_mana)
49             need_mana = (need_mana + 1) * DEC_MANA_DIV / MANA_DIV;
50     }
51
52 #undef DEC_MANA_DIV
53 #undef MANA_DIV
54 #undef MANA_CONST
55
56     return need_mana;
57 }
58
59 /*!
60  * @brief 呪文の失敗率修正処理1(呪い、消費魔力減少、呪文簡易化) /
61  * Modify spell fail rate
62  * Using to_m_chance, dec_mana, easy_spell and heavy_spell
63  * @param player_ptr プレイヤーへの参照ポインタ
64  * @param chance 修正前失敗率
65  * @return 失敗率(%)
66  * @todo 統合を検討
67  */
68 PERCENTAGE mod_spell_chance_1(player_type *player_ptr, PERCENTAGE chance)
69 {
70     chance += player_ptr->to_m_chance;
71
72     if (player_ptr->heavy_spell)
73         chance += 20;
74
75     if (player_ptr->dec_mana && player_ptr->easy_spell)
76         chance -= 4;
77     else if (player_ptr->easy_spell)
78         chance -= 3;
79     else if (player_ptr->dec_mana)
80         chance -= 2;
81
82     return chance;
83 }
84
85 /*!
86  * @brief 呪文の失敗率修正処理2(消費魔力減少、呪い、負値修正) /
87  * Modify spell fail rate
88  * Using to_m_chance, dec_mana, easy_spell and heavy_spell
89  * @param player_ptr プレイヤーへの参照ポインタ
90  * @param chance 修正前失敗率
91  * @return 失敗率(%)
92  * Modify spell fail rate (as "suffix" process)
93  * Using dec_mana, easy_spell and heavy_spell
94  * Note: variable "chance" cannot be negative.
95  * @todo 統合を検討
96  */
97 PERCENTAGE mod_spell_chance_2(player_type *player_ptr, PERCENTAGE chance)
98 {
99     if (player_ptr->dec_mana)
100         chance--;
101     if (player_ptr->heavy_spell)
102         chance += 5;
103     return std::max(chance, 0);
104 }
105
106 /*!
107  * @brief 呪文の失敗率計算メインルーチン /
108  * Returns spell chance of failure for spell -RAK-
109  * @param player_ptr プレイヤーへの参照ポインタ
110  * @param spell 呪文ID
111  * @param use_realm 魔法領域ID
112  * @return 失敗率(%)
113  */
114 PERCENTAGE spell_chance(player_type *player_ptr, SPELL_IDX spell, int16_t use_realm)
115 {
116     if (mp_ptr->spell_book == ItemKindType::NONE)
117         return 100;
118     if (use_realm == REALM_HISSATSU)
119         return 0;
120
121     const magic_type *s_ptr;
122     if (!is_magic(use_realm)) {
123         s_ptr = &technic_info[use_realm - MIN_TECHNIC][spell];
124     } else {
125         s_ptr = &mp_ptr->info[use_realm - 1][spell];
126     }
127
128     PERCENTAGE chance = s_ptr->sfail;
129     chance -= 3 * (player_ptr->lev - s_ptr->slevel);
130     chance -= 3 * (adj_mag_stat[player_ptr->stat_index[mp_ptr->spell_stat]] - 1);
131     if (player_ptr->riding)
132         chance += (std::max(r_info[player_ptr->current_floor_ptr->m_list[player_ptr->riding].r_idx].level - player_ptr->skill_exp[SKILL_RIDING] / 100 - 10, 0));
133
134     MANA_POINT need_mana = mod_need_mana(player_ptr, s_ptr->smana, spell, use_realm);
135     if (need_mana > player_ptr->csp) {
136         chance += 5 * (need_mana - player_ptr->csp);
137     }
138
139     if ((use_realm != player_ptr->realm1) && ((player_ptr->pclass == PlayerClassType::MAGE) || (player_ptr->pclass == PlayerClassType::PRIEST)))
140         chance += 5;
141
142     PERCENTAGE minfail = adj_mag_fail[player_ptr->stat_index[mp_ptr->spell_stat]];
143     if (any_bits(mp_ptr->spell_xtra, extra_min_magic_fail_rate)) {
144         if (minfail < 5)
145             minfail = 5;
146     }
147
148     if (((player_ptr->pclass == PlayerClassType::PRIEST) || (player_ptr->pclass == PlayerClassType::SORCERER)) && player_ptr->is_icky_wield[0])
149         chance += 25;
150     if (((player_ptr->pclass == PlayerClassType::PRIEST) || (player_ptr->pclass == PlayerClassType::SORCERER)) && player_ptr->is_icky_wield[1])
151         chance += 25;
152
153     chance = mod_spell_chance_1(player_ptr, chance);
154     PERCENTAGE penalty = (mp_ptr->spell_stat == A_WIS) ? 10 : 4;
155     switch (use_realm) {
156     case REALM_NATURE:
157         if ((player_ptr->alignment > 50) || (player_ptr->alignment < -50))
158             chance += penalty;
159         break;
160     case REALM_LIFE:
161     case REALM_CRUSADE:
162         if (player_ptr->alignment < -20)
163             chance += penalty;
164         break;
165     case REALM_DEATH:
166     case REALM_DAEMON:
167     case REALM_HEX:
168         if (player_ptr->alignment > 20)
169             chance += penalty;
170         break;
171     }
172
173     if (chance < minfail)
174         chance = minfail;
175
176     auto player_stun = player_ptr->effects()->stun();
177     chance += player_stun->get_magic_chance_penalty();
178     if (chance > 95) {
179         chance = 95;
180     }
181
182     if ((use_realm == player_ptr->realm1) || (use_realm == player_ptr->realm2) || (player_ptr->pclass == PlayerClassType::SORCERER)
183         || (player_ptr->pclass == PlayerClassType::RED_MAGE)) {
184         auto exp = PlayerSkill(player_ptr).exp_of_spell(use_realm, spell);
185         if (exp >= SPELL_EXP_EXPERT)
186             chance--;
187         if (exp >= SPELL_EXP_MASTER)
188             chance--;
189     }
190
191     return mod_spell_chance_2(player_ptr, chance);
192 }
193
194 /*!
195  * @brief 呪文情報の表示処理 /
196  * Print a list of spells (for browsing or casting or viewing)
197  * @param player_ptr 術者の参照ポインタ
198  * @param target_spell 呪文ID
199  * @param spells 表示するスペルID配列の参照ポインタ
200  * @param num 表示するスペルの数(spellsの要素数)
201  * @param y 表示メッセージ左上Y座標
202  * @param x 表示メッセージ左上X座標
203  * @param use_realm 魔法領域ID
204  */
205 void print_spells(player_type *player_ptr, SPELL_IDX target_spell, SPELL_IDX *spells, int num, TERM_LEN y, TERM_LEN x, int16_t use_realm)
206 {
207     if (((use_realm <= REALM_NONE) || (use_realm > MAX_REALM)) && allow_debug_options)
208         msg_print(_("警告! print_spell が領域なしに呼ばれた", "Warning! print_spells called with null realm"));
209
210     prt("", y, x);
211     char buf[256];
212     if (use_realm == REALM_HISSATSU)
213         strcpy(buf, _("  Lv   MP", "  Lv   SP"));
214     else
215         strcpy(buf, _("熟練度 Lv   MP 失率 効果", "Profic Lv   SP Fail Effect"));
216
217     put_str(_("名前", "Name"), y, x + 5);
218     put_str(buf, y, x + 29);
219
220     int increment = 64;
221     if ((player_ptr->pclass == PlayerClassType::SORCERER) || (player_ptr->pclass == PlayerClassType::RED_MAGE))
222         increment = 0;
223     else if (use_realm == player_ptr->realm1)
224         increment = 0;
225     else if (use_realm == player_ptr->realm2)
226         increment = 32;
227
228     int i;
229     int exp_level;
230     const magic_type *s_ptr;
231     char info[80];
232     char out_val[160];
233     char ryakuji[5];
234     bool max = false;
235     for (i = 0; i < num; i++) {
236         SPELL_IDX spell = spells[i];
237
238         if (!is_magic(use_realm)) {
239             s_ptr = &technic_info[use_realm - MIN_TECHNIC][spell];
240         } else {
241             s_ptr = &mp_ptr->info[use_realm - 1][spell];
242         }
243
244         MANA_POINT need_mana;
245         if (use_realm == REALM_HISSATSU)
246             need_mana = s_ptr->smana;
247         else {
248             auto exp = PlayerSkill(player_ptr).exp_of_spell(use_realm, spell);
249             need_mana = mod_need_mana(player_ptr, s_ptr->smana, spell, use_realm);
250             if ((increment == 64) || (s_ptr->slevel >= 99))
251                 exp_level = EXP_LEVEL_UNSKILLED;
252             else
253                 exp_level = PlayerSkill::spell_exp_level(exp);
254
255             max = false;
256             if (!increment && (exp_level == EXP_LEVEL_MASTER))
257                 max = true;
258             else if ((increment == 32) && (exp_level >= EXP_LEVEL_EXPERT))
259                 max = true;
260             else if (s_ptr->slevel >= 99)
261                 max = true;
262             else if ((player_ptr->pclass == PlayerClassType::RED_MAGE) && (exp_level >= EXP_LEVEL_SKILLED))
263                 max = true;
264
265             strncpy(ryakuji, exp_level_str[exp_level], 4);
266             ryakuji[3] = ']';
267             ryakuji[4] = '\0';
268         }
269
270         if (use_menu && target_spell) {
271             if (i == (target_spell - 1))
272                 strcpy(out_val, _("  》 ", "  >  "));
273             else
274                 strcpy(out_val, "     ");
275         } else
276             sprintf(out_val, "  %c) ", I2A(i));
277
278         if (s_ptr->slevel >= 99) {
279             strcat(out_val, format("%-30s", _("(判読不能)", "(illegible)")));
280             c_prt(TERM_L_DARK, out_val, y + i + 1, x);
281             continue;
282         }
283
284         strcpy(info, exe_spell(player_ptr, use_realm, spell, SPELL_INFO));
285         concptr comment = info;
286         byte line_attr = TERM_WHITE;
287         if ((player_ptr->pclass == PlayerClassType::SORCERER) || (player_ptr->pclass == PlayerClassType::RED_MAGE)) {
288             if (s_ptr->slevel > player_ptr->max_plv) {
289                 comment = _("未知", "unknown");
290                 line_attr = TERM_L_BLUE;
291             } else if (s_ptr->slevel > player_ptr->lev) {
292                 comment = _("忘却", "forgotten");
293                 line_attr = TERM_YELLOW;
294             }
295         } else if ((use_realm != player_ptr->realm1) && (use_realm != player_ptr->realm2)) {
296             comment = _("未知", "unknown");
297             line_attr = TERM_L_BLUE;
298         } else if ((use_realm == player_ptr->realm1) ? ((player_ptr->spell_forgotten1 & (1UL << spell))) : ((player_ptr->spell_forgotten2 & (1UL << spell)))) {
299             comment = _("忘却", "forgotten");
300             line_attr = TERM_YELLOW;
301         } else if (!((use_realm == player_ptr->realm1) ? (player_ptr->spell_learned1 & (1UL << spell)) : (player_ptr->spell_learned2 & (1UL << spell)))) {
302             comment = _("未知", "unknown");
303             line_attr = TERM_L_BLUE;
304         } else if (!((use_realm == player_ptr->realm1) ? (player_ptr->spell_worked1 & (1UL << spell)) : (player_ptr->spell_worked2 & (1UL << spell)))) {
305             comment = _("未経験", "untried");
306             line_attr = TERM_L_GREEN;
307         }
308
309         if (use_realm == REALM_HISSATSU) {
310             strcat(out_val, format("%-25s %2d %4d", exe_spell(player_ptr, use_realm, spell, SPELL_NAME), s_ptr->slevel, need_mana));
311         } else {
312             strcat(out_val,
313                 format("%-25s%c%-4s %2d %4d %3d%% %s", exe_spell(player_ptr, use_realm, spell, SPELL_NAME), (max ? '!' : ' '), ryakuji, s_ptr->slevel,
314                     need_mana, spell_chance(player_ptr, spell, use_realm), comment));
315         }
316
317         c_prt(line_attr, out_val, y + i + 1, x);
318     }
319
320     prt("", y + i + 1, x);
321 }