2 * @brief クラス、種族、突然変異に関するコマンド処理
7 #include "cmd-action/cmd-racial.h"
8 #include "action/action-limited.h"
9 #include "action/mutation-execution.h"
10 #include "action/racial-execution.h"
11 #include "core/asking-player.h"
12 #include "core/window-redrawer.h"
13 #include "game-option/text-display-options.h"
14 #include "io/command-repeater.h"
15 #include "io/input-key-acceptor.h"
16 #include "io/input-key-requester.h"
17 #include "main/sound-of-music.h"
18 #include "mutation/mutation-flag-types.h"
19 #include "player-base/player-class.h"
20 #include "player-info/race-info.h"
21 #include "player-info/samurai-data-type.h"
22 #include "player-status/player-energy.h"
23 #include "player/attack-defense-types.h"
24 #include "player/player-damage.h"
25 #include "player/special-defense-types.h"
26 #include "racial/class-racial-switcher.h"
27 #include "racial/mutation-racial-selector.h"
28 #include "racial/race-racial-command-setter.h"
29 #include "racial/racial-util.h"
30 #include "status/action-setter.h"
31 #include "system/angband-exceptions.h"
32 #include "system/player-type-definition.h"
33 #include "system/redrawing-flags-updater.h"
34 #include "term/screen-processor.h"
35 #include "term/z-form.h"
36 #include "util/bit-flags-calculator.h"
37 #include "util/int-char-converter.h"
38 #include "view/display-messages.h"
39 #include "view/display-util.h"
42 constexpr auto RC_PAGE_SIZE = 18;
44 static void racial_power_display_cursor(rc_type *rc_ptr)
46 auto y = rc_ptr->menu_line % RC_PAGE_SIZE;
47 put_str(_(" 》 ", " > "), 2 + y, 11);
50 static void racial_power_erase_cursor(rc_type *rc_ptr)
52 auto y = rc_ptr->menu_line % RC_PAGE_SIZE;
53 put_str(_(" ", " "), 2 + y, 11);
57 * @brief レイシャルパワー一覧を表示
58 * @param player_ptr プレイヤー情報への参照ポインタ
59 * @param rc_ptr レイシャルパワー情報への参照ポインタ
61 static void racial_power_display_list(PlayerType *player_ptr, rc_type *rc_ptr)
64 prt(_(" Lv MP 失率 効果", " Lv MP Fail Effect"), 1, x);
66 for (; y < RC_PAGE_SIZE; y++) {
67 auto ctr = RC_PAGE_SIZE * rc_ptr->page + y;
68 if (ctr >= rc_ptr->power_count()) {
80 letter = '0' + ctr - 26;
83 dummy = format(" %c) ", letter);
86 auto &rpi = rc_ptr->power_desc[ctr];
88 format("%-30.30s %2d %4d %3d%% %s", rpi.racial_name.data(), rpi.min_level, rpi.cost, 100 - racial_chance(player_ptr, &rc_ptr->power_desc[ctr]),
97 racial_power_display_cursor(rc_ptr);
102 * @brief レイシャルパワー選択用のプロンプトを作成する
103 * @param rc_ptr レイシャルパワー情報への参照ポインタ
105 static void racial_power_make_prompt(rc_type *rc_ptr)
109 if (rc_ptr->browse_mode) {
111 "(特殊能力 %c-%c, '*':一覧, '/'で使用, ESCで中断) どの能力について知りますか?", "(Powers %c-%c, *=List. /=Use, ESC=exit) Browse which power? ");
113 fmt = _("(特殊能力 %c-%c, '*'で一覧, '/'で閲覧, ESCで中断) どの能力を使いますか?", "(Powers %c-%c, *=List, /=Browse, ESC=exit) Use which power? ");
116 (void)strnfmt(rc_ptr->out_val, 78, fmt, I2A(0), (rc_ptr->power_count() <= 26) ? I2A(rc_ptr->power_count() - 1) : '0' + rc_ptr->power_count() - 27);
120 * @brief レイシャルパワー選択用のカーソル位置を進める
121 * @param player_ptr プレイヤー情報への参照ポインタ
122 * @param rc_ptr レイシャルパワー情報への参照ポインタ
125 static void racial_power_add_index(PlayerType *player_ptr, rc_type *rc_ptr, int i)
127 auto n = rc_ptr->menu_line + i;
128 if (i < -1 || i > 1) {
129 if (n < 0 || n >= rc_ptr->power_count()) {
135 n = rc_ptr->power_count() - 1;
138 if (n >= rc_ptr->power_count()) {
142 auto p = n / RC_PAGE_SIZE;
143 racial_power_erase_cursor(rc_ptr);
144 rc_ptr->menu_line = n;
145 if (rc_ptr->page != p) {
149 racial_power_display_list(player_ptr, rc_ptr);
151 racial_power_display_cursor(rc_ptr);
156 * @brief メニューによる選択のキーを処理する
157 * @param rc_ptr レイシャルパワー情報への参照ポインタ
158 * @return キャンセルならfalse、それ以外ならtrue
160 static bool racial_power_interpret_menu_keys(PlayerType *player_ptr, rc_type *rc_ptr)
162 switch (rc_ptr->choice) {
168 racial_power_add_index(player_ptr, rc_ptr, -1);
173 racial_power_add_index(player_ptr, rc_ptr, 1);
178 racial_power_add_index(player_ptr, rc_ptr, RC_PAGE_SIZE);
183 racial_power_add_index(player_ptr, rc_ptr, 0 - RC_PAGE_SIZE);
188 rc_ptr->command_code = (COMMAND_CODE)rc_ptr->menu_line;
189 rc_ptr->is_chosen = true;
193 rc_ptr->browse_mode = !rc_ptr->browse_mode;
194 racial_power_make_prompt(rc_ptr);
202 * @brief メニューからの選択決定を処理
203 * @param player_ptr プレイヤー情報への参照ポインタ
204 * @param rc_ptr レイシャルパワー情報への参照ポインタ
205 * @return キャンセルしたらfalse、それ以外ならtrue
207 static bool racial_power_select_by_menu(PlayerType *player_ptr, rc_type *rc_ptr)
209 if (!use_menu || rc_ptr->choice == ' ') {
213 if (!racial_power_interpret_menu_keys(player_ptr, rc_ptr)) {
217 if (rc_ptr->menu_line > rc_ptr->power_count()) {
218 rc_ptr->menu_line -= rc_ptr->power_count();
225 * @brief レイシャルパワーの選択を解釈
226 * @param player_ptr プレイヤー情報への参照ポインタ
227 * @param rc_ptr レイシャルパワー情報への参照ポインタ
228 * @return コマンド選択していたらtrue、していなかったらfalse
230 static bool racial_power_interpret_choise(PlayerType *player_ptr, rc_type *rc_ptr)
236 if (rc_ptr->choice == ' ' || rc_ptr->choice == '*') {
238 if (rc_ptr->page > rc_ptr->max_page) {
244 racial_power_display_list(player_ptr, rc_ptr);
248 if (rc_ptr->choice == '/') {
249 rc_ptr->browse_mode = !rc_ptr->browse_mode;
250 racial_power_make_prompt(rc_ptr);
254 if (rc_ptr->choice == '?') {
261 static void decide_racial_command(rc_type *rc_ptr)
267 if (rc_ptr->choice == '\r' && rc_ptr->power_count() == 1) {
268 rc_ptr->choice = 'a';
271 if (!isalpha(rc_ptr->choice)) {
273 rc_ptr->command_code = rc_ptr->choice - '0' + 26;
277 rc_ptr->ask = (isupper(rc_ptr->choice));
279 rc_ptr->choice = (char)tolower(rc_ptr->choice);
282 rc_ptr->command_code = (islower(rc_ptr->choice) ? A2I(rc_ptr->choice) : -1);
285 static bool ask_invoke_racial_power(rc_type *rc_ptr)
287 if ((rc_ptr->command_code < 0) || (rc_ptr->command_code >= rc_ptr->power_count())) {
297 (void)strnfmt(tmp_val, 78, _("%sを使いますか? ", "Use %s? "), rc_ptr->power_desc[rc_ptr->command_code].racial_name.data());
298 return input_check(tmp_val);
301 static void racial_power_display_explanation(PlayerType *player_ptr, rc_type *rc_ptr)
303 auto &rpi = rc_ptr->power_desc[rc_ptr->command_code];
311 display_wrap_around(rpi.text, 62, 17, 15);
313 prt(_("何かキーを押して下さい。", "Hit any key."), 0, 0);
318 racial_power_display_list(player_ptr, rc_ptr);
319 rc_ptr->is_chosen = false;
323 * @brief レイシャルパワー選択処理のメインループ
324 * @param player_ptr プレイヤー情報への参照ポインタ
325 * @param rc_ptr レイシャルパワー情報への参照ポインタ
326 * @return コマンド選択したらtrue、キャンセルしたらfalse
328 static bool racial_power_process_input(PlayerType *player_ptr, rc_type *rc_ptr)
330 rc_ptr->choice = (always_show_list || use_menu) ? ESCAPE : 1;
333 if (rc_ptr->choice == ESCAPE) {
334 rc_ptr->choice = ' ';
336 const auto choice = input_command(rc_ptr->out_val);
337 if (!choice.has_value()) {
341 rc_ptr->choice = choice.value();
344 if (!racial_power_select_by_menu(player_ptr, rc_ptr)) {
348 if (!rc_ptr->is_chosen && racial_power_interpret_choise(player_ptr, rc_ptr)) {
349 decide_racial_command(rc_ptr);
350 if (ask_invoke_racial_power(rc_ptr)) {
351 rc_ptr->is_chosen = true;
355 if (rc_ptr->is_chosen) {
356 if (rc_ptr->browse_mode) {
357 racial_power_display_explanation(player_ptr, rc_ptr);
368 * @brief レイシャル/クラスパワー選択を処理
369 * @param player_ptr プレイヤー情報への参照ポインタ
370 * @param rc_ptr レイシャルパワー情報への参照ポインタ
371 * @return コマンド選択したらtrue、キャンセルしたらfalse
373 static bool racial_power_select_power(PlayerType *player_ptr, rc_type *rc_ptr)
375 if (repeat_pull(&rc_ptr->command_code) && rc_ptr->command_code >= 0 && rc_ptr->command_code < rc_ptr->power_count()) {
381 racial_power_display_list(player_ptr, rc_ptr);
384 const auto is_selected = racial_power_process_input(player_ptr, rc_ptr);
390 repeat_push(rc_ptr->command_code);
395 * @brief レイシャルパワーの使用を試みる
396 * @param player_ptr プレイヤー情報への参照ポインタ
397 * @param rc_ptr レイシャルパワー情報への参照ポインタ
398 * @return レイシャルパワーの使用有無
400 static bool racial_power_cast_power(PlayerType *player_ptr, rc_type *rc_ptr)
402 auto *rpi_ptr = &rc_ptr->power_desc[rc_ptr->command_code];
403 switch (check_racial_level(player_ptr, rpi_ptr)) {
405 if (rpi_ptr->number < 0) {
406 return exe_racial_power(player_ptr, rpi_ptr->number);
409 return exe_mutation_power(player_ptr, i2enum<PlayerMutationType>(rpi_ptr->number));
415 THROW_EXCEPTION(std::logic_error, "Invalid racial level check!");
420 * @brief レイシャルパワーのコストを減らす
421 * @param player_ptr プレイヤー情報への参照ポインタ
422 * @param rc_ptr レイシャルパワー情報への参照ポインタ
423 * @return コストを減らしたらtrue、減らさなかったらfalse
426 * 戻り値はHP/MPの再描画が必要か判定するのに使用。
428 static bool racial_power_reduce_mana(PlayerType *player_ptr, rc_type *rc_ptr)
430 int racial_cost = rc_ptr->power_desc[rc_ptr->command_code].racial_cost;
431 if (racial_cost == 0) {
435 int actual_racial_cost = racial_cost / 2 + randint1(racial_cost / 2);
436 if (player_ptr->csp >= actual_racial_cost) {
437 player_ptr->csp -= actual_racial_cost;
439 actual_racial_cost -= player_ptr->csp;
441 take_hit(player_ptr, DAMAGE_USELIFE, actual_racial_cost, _("過度の集中", "concentrating too hard"));
448 * @brief レイシャル・パワーコマンドのメインルーチン
449 * @param player_ptr プレイヤーへの参照ポインタ
451 void do_cmd_racial_power(PlayerType *player_ptr)
453 if (player_ptr->wild_mode) {
457 PlayerEnergy energy(player_ptr);
458 if (cmd_limit_confused(player_ptr)) {
459 energy.reset_player_turn();
463 PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU, SamuraiStanceType::KOUKIJIN });
464 auto tmp_r = rc_type(player_ptr);
465 auto *rc_ptr = &tmp_r;
466 switch_class_racial(player_ptr, rc_ptr);
467 if (player_ptr->mimic_form != MimicKindType::NONE) {
468 set_mimic_racial_command(player_ptr, rc_ptr);
470 set_race_racial_command(player_ptr, rc_ptr);
473 select_mutation_racial(player_ptr, rc_ptr);
474 if (rc_ptr->power_count() == 0) {
475 msg_print(_("特殊能力はありません。", "You have no special powers."));
479 rc_ptr->max_page = 1 + (rc_ptr->power_count() - 1) / RC_PAGE_SIZE;
480 rc_ptr->page = use_menu ? 0 : -1;
481 racial_power_make_prompt(rc_ptr);
482 auto should_cast = false;
483 if (racial_power_select_power(player_ptr, rc_ptr)) {
484 should_cast = racial_power_cast_power(player_ptr, rc_ptr);
488 energy.reset_player_turn();
492 if (!racial_power_reduce_mana(player_ptr, rc_ptr)) {
496 auto &rfu = RedrawingFlagsUpdater::get_instance();
497 const auto &flags_mwrf = {
498 MainWindowRedrawingFlag::HP,
499 MainWindowRedrawingFlag::MP,
501 rfu.set_flags(flags_mwrf);
502 static constexpr auto flags_swrf = {
503 SubWindowRedrawingFlag::PLAYER,
504 SubWindowRedrawingFlag::SPELL,
506 rfu.set_flags(flags_swrf);