OSDN Git Service

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