OSDN Git Service

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