OSDN Git Service

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