2 * @file learnt-power-getter.cpp
6 #include "blue-magic/learnt-power-getter.h"
7 #include "blue-magic/blue-magic-checker.h"
8 #include "blue-magic/learnt-info.h"
9 #include "core/asking-player.h"
10 #include "core/stuff-handler.h"
11 #include "core/window-redrawer.h"
12 #include "game-option/text-display-options.h"
13 #include "io/command-repeater.h"
14 #include "io/input-key-acceptor.h"
15 #include "io/input-key-requester.h"
16 #include "main/sound-of-music.h"
17 #include "mind/mind-blue-mage.h"
18 #include "monster-race/race-ability-flags.h"
19 #include "mspell/monster-power-table.h"
20 #include "player-base/player-class.h"
21 #include "player-info/bluemage-data-type.h"
22 #include "player/player-status-table.h"
23 #include "realm/realm-types.h"
24 #include "spell/spell-info.h"
25 #include "system/player-type-definition.h"
26 #include "term/screen-processor.h"
27 #include "timed-effect/player-stun.h"
28 #include "timed-effect/timed-effects.h"
29 #include "util/enum-converter.h"
30 #include "util/flag-group.h"
31 #include "util/int-char-converter.h"
32 #include "util/string-processor.h"
33 #include "view/display-messages.h"
42 * @return 反復可能な青魔法があればそれを返す。なければ std::nullopt を返す。
44 static std::optional<RF_ABILITY> check_blue_magic_repeat()
47 if (!repeat_pull(&code)) {
51 if (auto spell = static_cast<RF_ABILITY>(code);
52 monster_powers.find(spell) != monster_powers.end()) {
60 * @brief 青魔法のタイプをコマンドメニューにより選択する
63 * 選択をキャンセルした場合は std::nullopt
65 static std::optional<BlueMagicType> select_blue_magic_type_by_menu()
68 std::optional<BlueMagicType> type;
72 while (!type.has_value()) {
73 prt(format(_(" %s ボルト", " %s bolt"), (menu_line == 1) ? _("》", "> ") : " "), 2, 14);
74 prt(format(_(" %s ボール", " %s ball"), (menu_line == 2) ? _("》", "> ") : " "), 3, 14);
75 prt(format(_(" %s ブレス", " %s breath"), (menu_line == 3) ? _("》", "> ") : " "), 4, 14);
76 prt(format(_(" %s 召喚", " %s sommoning"), (menu_line == 4) ? _("》", "> ") : " "), 5, 14);
77 prt(format(_(" %s その他", " %s others"), (menu_line == 5) ? _("》", "> ") : " "), 6, 14);
78 prt(_("どの種類の魔法を使いますか?", "use which type of magic? "), 0, 0);
80 auto choice = inkey();
101 type = i2enum<BlueMagicType>(menu_line);
116 * @brief 青魔法のタイプを記号により選択する
118 * @return 選択した青魔法のタイプ
119 * 選択をキャンセルした場合は std::nullopt
121 static std::optional<BlueMagicType> select_blue_magic_kind_by_symbol()
123 auto candidate_desc = _("[A]ボルト, [B]ボール, [C]ブレス, [D]召喚, [E]その他:", "[A] bolt, [B] ball, [C] breath, [D] summoning, [E] others:");
127 if (!get_com(candidate_desc, &ch, true)) {
134 return BlueMagicType::BOLT;
137 return BlueMagicType::BALL;
140 return BlueMagicType::BREATH;
143 return BlueMagicType::SUMMON;
146 return BlueMagicType::OTHER;
156 * @brief 指定したタイプの青魔法のリストを(覚えていないものも含め)返す
158 * @param bluemage_data 青魔道士の固有データへの参照
159 * @param type 青魔法のタイプ
160 * @return 指定したタイプの青魔法のリストを(覚えていないものも含め)返す
161 * 但し、そのタイプの魔法を1つも覚えていない場合は std::nullopt を返す
163 static std::optional<std::vector<RF_ABILITY>> sweep_learnt_spells(const bluemage_data_type &bluemage_data, BlueMagicType type)
165 EnumClassFlagGroup<RF_ABILITY> ability_flags;
166 set_rf_masks(ability_flags, type);
168 if (bluemage_data.learnt_blue_magics.has_none_of(ability_flags)) {
169 msg_print(_("その種類の魔法は覚えていない!", "You don't know any spell of this type."));
173 std::vector<RF_ABILITY> blue_magics;
174 EnumClassFlagGroup<RF_ABILITY>::get_flags(ability_flags, std::back_inserter(blue_magics));
180 * @brief 入力されたキーに従いコマンドメニューで選択中の青魔法を切り替える
183 * @param menu_line 選択中の青魔法の行
184 * @param bluemage_data 青魔道士の固有データへの参照
185 * @param blue_magics 青魔法のリスト(覚えていないものも含まれているが、カーソル移動時に選択をスキップする)
186 * @return 選択確定キーが入力された場合は true、そうでなければ false
188 static bool switch_blue_magic_choice(char key, int &menu_line, const bluemage_data_type &bluemage_data, const std::vector<RF_ABILITY> blue_magics)
190 const auto &learnt_blue_magics = bluemage_data.learnt_blue_magics;
191 const int blue_magics_count = blue_magics.size();
198 menu_line += (blue_magics_count - 1);
199 if (menu_line > blue_magics_count) {
200 menu_line -= blue_magics_count;
202 } while (learnt_blue_magics.has_not(blue_magics[menu_line - 1]));
210 if (menu_line > blue_magics_count) {
211 menu_line -= blue_magics_count;
213 } while (learnt_blue_magics.has_not(blue_magics[menu_line - 1]));
219 menu_line = blue_magics_count;
220 while (learnt_blue_magics.has_not(blue_magics[menu_line - 1])) {
230 while (learnt_blue_magics.has_not(blue_magics[menu_line - 1])) {
247 * @brief 青魔法の失敗率を計算する
249 * @param mp 失敗率を計算する青魔法に対応した monster_power 構造体への参照
250 * @param need_mana 青魔法を使うのに必要なMP
251 * @return int 失敗率(%)を返す
253 static int calculate_blue_magic_failure_probability(player_type *player_ptr, const monster_power &mp, int need_mana)
255 auto chance = mp.fail;
256 if (player_ptr->lev > mp.level) {
257 chance -= 3 * (player_ptr->lev - mp.level);
259 chance += (mp.level - player_ptr->lev);
262 chance -= 3 * (adj_mag_stat[player_ptr->stat_index[A_INT]] - 1);
263 chance = mod_spell_chance_1(player_ptr, chance);
264 if (need_mana > player_ptr->csp) {
265 chance += 5 * (need_mana - player_ptr->csp);
268 PERCENTAGE minfail = adj_mag_fail[player_ptr->stat_index[A_INT]];
269 if (chance < minfail) {
273 auto player_stun = player_ptr->effects()->stun();
274 chance += player_stun->get_magic_chance_penalty();
279 chance = mod_spell_chance_2(player_ptr, chance);
285 * @brief 青魔法リストに表示する青魔法の先頭のヘッダを出力する
286 * 記号で選択する場合、" 記号)" が出力される
287 * コマンドメニューで選択する場合、選択中の青魔法にはカーソルが、そうでない青魔法には空白が出力される
289 * @param buf 出力するバッファ
290 * @param buf_size バッファのサイズ
291 * @param index 選択する青魔法のリスト上の番号
292 * @param menu_line コマンドメニューで選択する場合、選択中の行。記号で選択する場合は使用されない。
294 static void close_blue_magic_name(char *buf, size_t buf_size, int index, int menu_line)
297 snprintf(buf, buf_size, " %c)", I2A(index));
301 if (index == menu_line - 1) {
302 snprintf(buf, buf_size, _(" 》", " > "));
304 snprintf(buf, buf_size, " ");
309 * @brief 使用できる青魔法のリストを表示する
311 * @param menu_line 選択中の青魔法の行
312 * @param bluemage_data 青魔道士の固有データへの参照
313 * @param blue_magics 青魔法のリスト(覚えていないものも含まれているが、覚えていないものは表示をスキップする)
315 static void describe_blue_magic_name(player_type *player_ptr, int menu_line, const bluemage_data_type &bluemage_data, const std::vector<RF_ABILITY> &blue_magics)
317 constexpr TERM_LEN y_base = 1;
318 constexpr TERM_LEN x_base = 18;
319 prt("", y_base, x_base);
320 put_str(_("名前", "Name"), y_base, x_base + 5);
321 put_str(_("MP 失率 効果", "SP Fail Info"), y_base, x_base + 33);
322 for (auto i = 0U; i < blue_magics.size(); ++i) {
323 prt("", y_base + i + 1, x_base);
324 const auto &spell = blue_magics[i];
325 if (bluemage_data.learnt_blue_magics.has_not(spell)) {
329 const auto &mp = monster_powers.at(spell);
330 auto need_mana = mod_need_mana(player_ptr, mp.smana, 0, REALM_NONE);
331 auto chance = calculate_blue_magic_failure_probability(player_ptr, mp, need_mana);
333 learnt_info(player_ptr, comment, spell);
335 close_blue_magic_name(psi_desc, sizeof(psi_desc), i, menu_line);
336 angband_strcat(psi_desc, format(" %-26s %3d %3d%%%s", mp.name, need_mana, chance, comment), sizeof(psi_desc));
337 prt(psi_desc, y_base + i + 1, x_base);
340 prt("", y_base + blue_magics.size() + 1, x_base);
344 * @brief 青魔法を唱えるか確認する
346 * @param spell 唱える青魔法
347 * @return 唱えるなら ture、キャンセルするなら false を返す
349 static bool confirm_cast_blue_magic(RF_ABILITY spell)
352 (void)strnfmt(tmp_val, 78, _("%sの魔法を唱えますか?", "Use %s? "), monster_powers.at(spell).name);
353 return get_check(tmp_val);
357 * @brief 唱える青魔法を記号により選択する
359 * @param bluemage_data 青魔道士の固有データへの参照
360 * @param blue_magics 青魔法のリスト(覚えていないものも含まれているが、覚えていない物は候補に出ず選択できない)
361 * @return 選択した青魔法。選択をキャンセルした場合は std::nullopt
363 static std::optional<RF_ABILITY> select_learnt_spells_by_symbol(player_type *player_ptr, const bluemage_data_type &bluemage_data, std::vector<RF_ABILITY> spells)
366 (void)strnfmt(out_val, sizeof(out_val), _("(%c-%c, '*'で一覧, ESC) どの%sを唱えますか?", "(%c-%c, *=List, ESC=exit) Use which %s? "),
367 I2A(0), I2A(spells.size() - 1), _("魔法", "magic"));
369 bool first_show_list = always_show_list;
370 auto show_list = false;
371 std::optional<RF_ABILITY> selected_spell;
373 while (!selected_spell.has_value()) {
375 if (!first_show_list && !get_com(out_val, &choice, true)) {
379 if (first_show_list || (choice == ' ') || (choice == '*') || (choice == '?')) {
380 // 選択する青魔法一覧の表示/非表示切り替え
381 first_show_list = false;
382 show_list = !show_list;
385 describe_blue_magic_name(player_ptr, 0, bluemage_data, spells);
392 auto confirm = isupper(choice) != 0;
393 uint index = A2I(tolower(choice));
394 if (spells.size() <= index || bluemage_data.learnt_blue_magics.has_not(spells[index])) {
399 if (confirm && !confirm_cast_blue_magic(spells[index])) {
403 selected_spell = spells[index];
410 return selected_spell;
414 * @brief 唱える青魔法をコマンドメニューにより選択する
416 * @param bluemage_data 青魔道士の固有データへの参照
417 * @param blue_magics 青魔法のリスト(覚えていないものも含まれているが、覚えていない物は候補に出ず選択できない)
418 * @return 選択した青魔法。選択をキャンセルした場合は std::nullopt
420 static std::optional<RF_ABILITY> select_learnt_spells_by_menu(player_type *player_ptr, const bluemage_data_type &bluemage_data, std::vector<RF_ABILITY> spells)
423 angband_strcpy(out_val, _("(ESC=中断) どの魔法を唱えますか?", "(ESC=exit) Use which magic? "), sizeof(out_val));
425 auto it = std::find_if(spells.begin(), spells.end(), [&bluemage_data](const auto &spell) { return bluemage_data.learnt_blue_magics.has(spell); });
426 int menu_line = std::distance(spells.begin(), it) + 1;
427 std::optional<RF_ABILITY> selected_spell;
431 while (!selected_spell.has_value()) {
432 describe_blue_magic_name(player_ptr, menu_line, bluemage_data, spells);
435 if (!get_com(out_val, &choice, true) || choice == '0') {
439 if (!switch_blue_magic_choice(choice, menu_line, bluemage_data, spells)) {
443 uint index = menu_line - 1;
444 if (spells.size() <= index || bluemage_data.learnt_blue_magics.has_not(spells[index])) {
449 selected_spell = spells[index];
454 return selected_spell;
458 * @brief 使用可能な青魔法を選択する /
459 * Allow user to choose a imitation.
460 * @param player_ptr プレイヤーへの参照ポインタ
461 * @param sn 選択したモンスター攻撃ID、キャンセルの場合-1、不正な選択の場合-2を返す
462 * @return 発動可能な魔法を選択した場合TRUE、キャンセル処理か不正な選択が行われた場合FALSEを返す。
464 * If a valid spell is chosen, saves it in '*sn' and returns TRUE\n
465 * If the user hits escape, returns FALSE, and set '*sn' to -1\n
466 * If there are no legal choices, returns FALSE, and sets '*sn' to -2\n
468 * The "prompt" should be "cast", "recite", or "study"\n
469 * The "known" should be TRUE for cast/pray, FALSE for study\n
471 * nb: This function has a (trivial) display bug which will be obvious\n
472 * when you run it. It's probably easy to fix but I haven't tried,\n
475 std::optional<RF_ABILITY> get_learned_power(player_type *player_ptr)
477 auto bluemage_data = PlayerClass(player_ptr).get_specific_data<bluemage_data_type>();
478 if (!bluemage_data) {
482 if (auto repeat_spell = check_blue_magic_repeat();
483 repeat_spell.has_value()) {
487 auto type = (use_menu)
488 ? select_blue_magic_type_by_menu()
489 : select_blue_magic_kind_by_symbol();
490 if (!type.has_value()) {
494 auto spells = sweep_learnt_spells(*bluemage_data, type.value());
495 if (!spells.has_value() || spells->empty()) {
499 auto selected_spell = (use_menu)
500 ? select_learnt_spells_by_menu(player_ptr, *bluemage_data, spells.value())
501 : select_learnt_spells_by_symbol(player_ptr, *bluemage_data, spells.value());
503 player_ptr->window_flags |= PW_SPELL;
504 handle_stuff(player_ptr);
506 if (!selected_spell.has_value()) {
510 repeat_push(static_cast<COMMAND_CODE>(selected_spell.value()));
511 return selected_spell;