OSDN Git Service

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