OSDN Git Service

761f087d3af74d724f8297a4f2f7001032226fd3
[hengbandforosx/hengbandosx.git] / src / mind / mind-power-getter.cpp
1 #include "mind/mind-power-getter.h"
2 #include "core/asking-player.h"
3 #include "core/stuff-handler.h"
4 #include "core/window-redrawer.h"
5 #include "game-option/text-display-options.h"
6 #include "inventory/inventory-slot-types.h"
7 #include "io/command-repeater.h"
8 #include "io/input-key-requester.h"
9 #include "main/sound-of-music.h"
10 #include "mind/mind-explanations-table.h"
11 #include "mind/mind-force-trainer.h"
12 #include "mind/mind-info.h"
13 #include "mind/mind-types.h"
14 #include "player-info/class-info.h"
15 #include "player-info/equipment-info.h"
16 #include "player/player-status-table.h"
17 #include "system/player-type-definition.h"
18 #include "term/screen-processor.h"
19 #include "timed-effect/player-stun.h"
20 #include "timed-effect/timed-effects.h"
21 #include "util/enum-converter.h"
22 #include "util/int-char-converter.h"
23
24 MindPowerGetter::MindPowerGetter(player_type *player_ptr)
25     : player_ptr(player_ptr)
26     , menu_line(use_menu ? 1 : 0)
27 {
28 }
29
30 /*!
31  * @brief 使用可能な特殊技能を選択する /
32  * Allow user to choose a mindcrafter power.
33  * @param sn 選択した特殊技能ID、キャンセルの場合-1、不正な選択の場合-2を返す
34  * @param only_browse 一覧を見るだけの場合trueを返す
35  * @return 発動可能な魔法を選択した場合true、キャンセル処理か不正な選択が行われた場合falseを返す。
36  * @details
37  * If a valid spell is chosen, saves it in '*sn' and returns true\n
38  * If the user hits escape, returns false, and set '*sn' to -1\n
39  * If there are no legal choices, returns false, and sets '*sn' to -2\n
40  *\n
41  * The "prompt" should be "cast", "recite", or "study"\n
42  * The "known" should be true for cast/pray, false for study\n
43  *\n
44  * nb: This function has a (trivial) display bug which will be obvious\n
45  * when you run it. It's probably easy to fix but I haven't tried,\n
46  * sorry.\n
47  */
48 bool MindPowerGetter::get_mind_power(SPELL_IDX *sn, bool only_browse)
49 {
50     select_mind_description();
51     if (select_spell_index(sn)) {
52         return true;
53     }
54
55     for (this->index = 0; this->index < MAX_MIND_POWERS; this->index++) {
56         if (mind_ptr->info[this->index].min_lev <= this->player_ptr->lev) {
57             this->num++;
58         }
59     }
60
61     char out_val[160];
62     if (only_browse) {
63         (void)strnfmt(out_val, 78, _("(%^s %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
64             this->mind_description, I2A(0), I2A(this->num - 1), this->mind_description);
65     } else {
66         (void)strnfmt(out_val, 78, _("(%^s %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
67             this->mind_description, I2A(0), I2A(this->num - 1), this->mind_description);
68     }
69
70     if (use_menu && !only_browse) {
71         screen_save();
72     }
73
74     this->choice = (always_show_list || use_menu) ? ESCAPE : 1;
75     decide_mind_choice(out_val, only_browse);
76     if (this->redraw && !only_browse) {
77         screen_load();
78     }
79
80     this->player_ptr->window_flags |= PW_SPELL;
81     handle_stuff(this->player_ptr);
82     if (!this->flag) {
83         return false;
84     }
85
86     *sn = this->index;
87     repeat_push((COMMAND_CODE)this->index);
88     return true;
89 }
90
91 /*
92  * @brief 職業ごとの特殊技能表記を取得する
93  */
94 void MindPowerGetter::select_mind_description()
95 {
96     switch (this->player_ptr->pclass)
97     case PlayerClassType::MINDCRAFTER: {
98         this->use_mind = MindKindType::MINDCRAFTER;
99         this->mind_description = _("超能力", "mindcraft");
100         break;
101     case PlayerClassType::FORCETRAINER:
102         this->use_mind = MindKindType::KI;
103         this->mind_description = _("練気術", "Force");
104         break;
105     case PlayerClassType::BERSERKER:
106         this->use_mind = MindKindType::BERSERKER;
107         this->mind_description = _("技", "brutal power");
108         break;
109     case PlayerClassType::MIRROR_MASTER:
110         this->use_mind = MindKindType::MIRROR_MASTER;
111         this->mind_description = _("鏡魔法", "magic");
112         break;
113     case PlayerClassType::NINJA:
114         this->use_mind = MindKindType::NINJUTSU;
115         this->mind_description = _("忍術", "ninjutsu");
116         break;
117     default:
118         this->use_mind = MindKindType::MINDCRAFTER;
119         this->mind_description = _("超能力", "mindcraft");
120         break;
121     }
122 }
123
124 bool MindPowerGetter::select_spell_index(SPELL_IDX *sn)
125 {
126     COMMAND_CODE code;
127     this->mind_ptr = &mind_powers[enum2i(this->use_mind)];
128     *sn = -1;
129     if (!repeat_pull(&code)) {
130         return false;
131     }
132
133     *sn = (SPELL_IDX)code;
134     if (*sn == INVEN_FORCE) {
135         (void)repeat_pull(&code);
136     }
137
138     *sn = (SPELL_IDX)code;
139     return mind_ptr->info[*sn].min_lev <= this->player_ptr->lev;
140 }
141
142 bool MindPowerGetter::decide_mind_choice(char *out_val, const bool only_browse)
143 {
144     while (!this->flag) {
145         if (this->choice == ESCAPE) {
146             this->choice = ' ';
147         } else if (!get_com(out_val, &this->choice, true)) {
148             break;
149         }
150
151         if (!interpret_mind_key_input(only_browse)) {
152             return false;
153         }
154
155         if (display_minds_chance(only_browse)) {
156             continue;
157         }
158
159         make_choice_lower();
160         if ((this->index < 0) || (this->index >= this->num)) {
161             bell();
162             continue;
163         }
164
165         this->spell = &mind_ptr->info[this->index];
166         if (this->ask) {
167             char tmp_val[160];
168             (void)strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), this->spell->name);
169             if (!get_check(tmp_val)) {
170                 continue;
171             }
172         }
173
174         this->flag = true;
175     }
176
177     return true;
178 }
179
180 bool MindPowerGetter::interpret_mind_key_input(const bool only_browse)
181 {
182     if (!use_menu || this->choice == ' ') {
183         return true;
184     }
185
186     switch (this->choice) {
187     case '0':
188         if (!only_browse) {
189             screen_load();
190         }
191
192         return false;
193     case '8':
194     case 'k':
195     case 'K':
196         this->menu_line += (this->num - 1);
197         break;
198     case '2':
199     case 'j':
200     case 'J':
201         this->menu_line++;
202         break;
203     case 'x':
204     case 'X':
205     case '\r':
206     case '\n':
207         this->index = this->menu_line - 1;
208         this->ask = false;
209         break;
210     default:
211         break;
212     }
213
214     if (this->menu_line > this->num) {
215         this->menu_line -= this->num;
216     }
217
218     return true;
219 }
220
221 bool MindPowerGetter::display_minds_chance(const bool only_browse)
222 {
223     if ((this->choice != ' ') && (this->choice != '*') && (this->choice != '?') && (!use_menu || !this->ask)) {
224         return false;
225     }
226
227     if (!this->redraw || use_menu) {
228         this->redraw = true;
229         if (!only_browse && !use_menu) {
230             screen_save();
231         }
232
233         prt("", y, x);
234         put_str(_("名前", "Name"), y, x + 5);
235         put_str(format(_("Lv   %s   失率 効果", "Lv   %s   Fail Info"),
236                     ((this->use_mind == MindKindType::BERSERKER) || (this->use_mind == MindKindType::NINJUTSU)) ? "HP" : "MP"),
237             y, x + 35);
238         display_each_mind_chance();
239         prt("", y + this->index + 1, x);
240         return true;
241     }
242
243     if (only_browse) {
244         return true;
245     }
246
247     this->redraw = false;
248     screen_load();
249     return true;
250 }
251
252 void MindPowerGetter::display_each_mind_chance()
253 {
254     bool has_weapon[2];
255     has_weapon[0] = has_melee_weapon(this->player_ptr, INVEN_MAIN_HAND);
256     has_weapon[1] = has_melee_weapon(this->player_ptr, INVEN_SUB_HAND);
257     for (this->index = 0; this->index < MAX_MIND_POWERS; this->index++) {
258         this->spell = &mind_ptr->info[this->index];
259         if (this->spell->min_lev > this->player_ptr->lev) {
260             break;
261         }
262
263         calculate_mind_chance(has_weapon);
264         char comment[80];
265         mindcraft_info(this->player_ptr, comment, this->use_mind, this->index);
266         char psi_desc[80];
267         if (use_menu) {
268             if (this->index == (this->menu_line - 1)) {
269                 strcpy(psi_desc, _("  》 ", "  >  "));
270             } else {
271                 strcpy(psi_desc, "     ");
272             }
273         } else {
274             sprintf(psi_desc, "  %c) ", I2A(this->index));
275         }
276
277         strcat(psi_desc,
278             format("%-30s%2d %4d%s %3d%%%s", this->spell->name, this->spell->min_lev, mana_cost,
279                 (((this->use_mind == MindKindType::MINDCRAFTER) && (this->index == 13)) ? _("~", "~ ") : "  "), chance, comment));
280         prt(psi_desc, y + this->index + 1, x);
281     }
282 }
283
284 void MindPowerGetter::calculate_mind_chance(bool *has_weapon)
285 {
286     this->chance = this->spell->fail;
287     this->mana_cost = this->spell->mana_cost;
288     if (this->chance == 0) {
289         return;
290     }
291
292     this->chance -= 3 * (this->player_ptr->lev - this->spell->min_lev);
293     this->chance -= 3 * (adj_mag_stat[this->player_ptr->stat_index[mp_ptr->spell_stat]] - 1);
294     calculate_ki_chance(has_weapon);
295     if ((this->use_mind != MindKindType::BERSERKER) && (this->use_mind != MindKindType::NINJUTSU) && (this->mana_cost > this->player_ptr->csp)) {
296         this->chance += 5 * (this->mana_cost - this->player_ptr->csp);
297     }
298
299     this->chance += this->player_ptr->to_m_chance;
300     PERCENTAGE minfail = adj_mag_fail[this->player_ptr->stat_index[mp_ptr->spell_stat]];
301     if (this->chance < minfail) {
302         this->chance = minfail;
303     }
304
305     auto player_stun = this->player_ptr->effects()->stun();
306     this->chance += player_stun->get_magic_chance_penalty();
307     add_ki_chance();
308     if (this->chance > 95) {
309         this->chance = 95;
310     }
311 }
312
313 void MindPowerGetter::calculate_ki_chance(bool *has_weapon)
314 {
315     if (this->use_mind != MindKindType::KI) {
316         return;
317     }
318
319     if (heavy_armor(this->player_ptr))
320         this->chance += 20;
321
322     if (this->player_ptr->is_icky_wield[0]) {
323         this->chance += 20;
324     } else if (has_weapon[0]) {
325         this->chance += 10;
326     }
327
328     if (this->player_ptr->is_icky_wield[1]) {
329         chance += 20;
330     } else if (has_weapon[1]) {
331         this->chance += 10;
332     }
333
334     if (this->index == 5) {
335         for (auto j = 0; j < get_current_ki(this->player_ptr) / 50; j++) {
336             this->mana_cost += (j + 1) * 3 / 2;
337         }
338     }
339 }
340
341 void MindPowerGetter::add_ki_chance()
342 {
343     if (this->use_mind != MindKindType::KI) {
344         return;
345     }
346
347     if (heavy_armor(this->player_ptr)) {
348         this->chance += 5;
349     }
350
351     if (this->player_ptr->is_icky_wield[0]) {
352         this->chance += 5;
353     }
354
355     if (this->player_ptr->is_icky_wield[1]) {
356         this->chance += 5;
357     }
358 }
359
360 void MindPowerGetter::make_choice_lower()
361 {
362     if (use_menu) {
363         return;
364     }
365
366     this->ask = (bool)isupper(this->choice);
367     if (this->ask) {
368         this->choice = (char)tolower(this->choice);
369     }
370
371     this->index = (islower(this->choice) ? A2I(this->choice) : -1);
372 }