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"
25 MindPowerGetter::MindPowerGetter(PlayerType *player_ptr)
26 : player_ptr(player_ptr)
27 , menu_line(use_menu ? 1 : 0)
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を返す。
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
42 * The "prompt" should be "cast", "recite", or "study"\n
43 * The "known" should be true for cast/pray, false for study\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
49 bool MindPowerGetter::get_mind_power(SPELL_IDX *sn, bool only_browse)
51 select_mind_description();
52 if (select_spell_index(sn)) {
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) {
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);
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);
71 if (use_menu && !only_browse) {
75 this->choice = (always_show_list || use_menu) ? ESCAPE : 1;
76 decide_mind_choice(out_val, only_browse);
77 if (this->redraw && !only_browse) {
81 this->player_ptr->window_flags |= PW_SPELL;
82 handle_stuff(this->player_ptr);
88 repeat_push((COMMAND_CODE)this->index);
93 * @brief 職業ごとの特殊技能表記を取得する
95 void MindPowerGetter::select_mind_description()
97 switch (this->player_ptr->pclass)
98 case PlayerClassType::MINDCRAFTER: {
99 this->use_mind = MindKindType::MINDCRAFTER;
100 this->mind_description = _("超能力", "mindcraft");
102 case PlayerClassType::FORCETRAINER:
103 this->use_mind = MindKindType::KI;
104 this->mind_description = _("練気術", "Force");
106 case PlayerClassType::BERSERKER:
107 this->use_mind = MindKindType::BERSERKER;
108 this->mind_description = _("技", "brutal power");
110 case PlayerClassType::MIRROR_MASTER:
111 this->use_mind = MindKindType::MIRROR_MASTER;
112 this->mind_description = _("鏡魔法", "magic");
114 case PlayerClassType::NINJA:
115 this->use_mind = MindKindType::NINJUTSU;
116 this->mind_description = _("忍術", "ninjutsu");
119 this->use_mind = MindKindType::MINDCRAFTER;
120 this->mind_description = _("超能力", "mindcraft");
125 bool MindPowerGetter::select_spell_index(SPELL_IDX *sn)
128 this->mind_ptr = &mind_powers[enum2i(this->use_mind)];
130 if (!repeat_pull(&code)) {
134 *sn = (SPELL_IDX)code;
135 if (*sn == INVEN_FORCE) {
136 (void)repeat_pull(&code);
139 *sn = (SPELL_IDX)code;
140 return mind_ptr->info[*sn].min_lev <= this->player_ptr->lev;
143 bool MindPowerGetter::decide_mind_choice(char *out_val, const bool only_browse)
145 while (!this->flag) {
146 if (this->choice == ESCAPE) {
148 } else if (!get_com(out_val, &this->choice, true)) {
152 if (!interpret_mind_key_input(only_browse)) {
156 if (display_minds_chance(only_browse)) {
161 if ((this->index < 0) || (this->index >= this->num)) {
166 this->spell = &mind_ptr->info[this->index];
173 bool MindPowerGetter::interpret_mind_key_input(const bool only_browse)
175 if (!use_menu || this->choice == ' ') {
179 this->should_redraw_cursor = true;
180 switch (this->choice) {
190 this->menu_line += (this->num - 1);
201 this->index = this->menu_line - 1;
202 this->should_redraw_cursor = false;
208 if (this->menu_line > this->num) {
209 this->menu_line -= this->num;
215 bool MindPowerGetter::display_minds_chance(const bool only_browse)
217 if ((this->choice != ' ') && (this->choice != '*') && (this->choice != '?') && (!use_menu || !this->should_redraw_cursor)) {
221 if (!this->redraw || use_menu) {
223 if (!only_browse && !use_menu) {
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"),
232 display_each_mind_chance();
233 prt("", y + this->index + 1, x);
241 this->redraw = false;
246 void MindPowerGetter::display_each_mind_chance()
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) {
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;
261 if (this->index == (this->menu_line - 1)) {
262 psi_desc = _(" 》 ", " > ");
267 psi_desc = format(" %c) ", I2A(this->index));
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);
276 void MindPowerGetter::calculate_mind_chance(bool *has_weapon)
278 this->chance = this->spell->fail;
279 this->mana_cost = this->spell->mana_cost;
280 if (this->chance == 0) {
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);
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;
297 auto player_stun = this->player_ptr->effects()->stun();
298 this->chance += player_stun->get_magic_chance_penalty();
300 if (this->chance > 95) {
305 void MindPowerGetter::calculate_ki_chance(bool *has_weapon)
307 if (this->use_mind != MindKindType::KI) {
311 if (heavy_armor(this->player_ptr)) {
315 if (this->player_ptr->is_icky_wield[0]) {
317 } else if (has_weapon[0]) {
321 if (this->player_ptr->is_icky_wield[1]) {
323 } else if (has_weapon[1]) {
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;
334 void MindPowerGetter::add_ki_chance()
336 if (this->use_mind != MindKindType::KI) {
340 if (heavy_armor(this->player_ptr)) {
344 if (this->player_ptr->is_icky_wield[0]) {
348 if (this->player_ptr->is_icky_wield[1]) {
353 void MindPowerGetter::make_choice_lower()
359 this->index = A2I(this->choice);