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/player-redraw-types.h"
13 #include "core/window-redrawer.h"
14 #include "game-option/text-display-options.h"
15 #include "io/command-repeater.h"
16 #include "io/input-key-acceptor.h"
17 #include "io/input-key-requester.h"
18 #include "main/sound-of-music.h"
19 #include "mutation/mutation-flag-types.h"
20 #include "player-base/player-class.h"
21 #include "player-info/race-info.h"
22 #include "player-info/samurai-data-type.h"
23 #include "player-status/player-energy.h"
24 #include "player/attack-defense-types.h"
25 #include "player/player-damage.h"
26 #include "player/special-defense-types.h"
27 #include "racial/class-racial-switcher.h"
28 #include "racial/mutation-racial-selector.h"
29 #include "racial/race-racial-command-setter.h"
30 #include "racial/racial-util.h"
31 #include "status/action-setter.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 #define RC_PAGE_SIZE 18
44 #define RC_CANCEL true
45 #define RC_CONTINUE false
47 static void racial_power_display_cursor(rc_type *rc_ptr)
49 auto y = rc_ptr->menu_line % RC_PAGE_SIZE;
50 put_str(_(" 》 ", " > "), 2 + y, 11);
53 static void racial_power_erase_cursor(rc_type *rc_ptr)
55 auto y = rc_ptr->menu_line % RC_PAGE_SIZE;
56 put_str(_(" ", " "), 2 + y, 11);
60 * @brief レイシャルパワー一覧を表示
61 * @param player_ptr プレイヤー情報への参照ポインタ
62 * @param rc_ptr レイシャルパワー情報への参照ポインタ
63 * @return キャンセルしたらRC_CANCEL、それ以外ならRC_CONTINUE
65 static void racial_power_display_list(PlayerType *player_ptr, rc_type *rc_ptr)
69 prt(_(" Lv MP 失率 効果", " Lv MP Fail Effect"), 1, x);
72 for (; y < RC_PAGE_SIZE; y++) {
73 auto ctr = RC_PAGE_SIZE * rc_ptr->page + y;
74 if (ctr >= rc_ptr->power_count()) {
86 letter = '0' + ctr - 26;
89 dummy = format(" %c) ", letter);
92 auto &rpi = rc_ptr->power_desc[ctr];
94 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]),
104 racial_power_display_cursor(rc_ptr);
109 * @brief レイシャルパワー選択用のプロンプトを作成する
110 * @param rc_ptr レイシャルパワー情報への参照ポインタ
112 static void racial_power_make_prompt(rc_type *rc_ptr)
116 if (rc_ptr->browse_mode) {
118 "(特殊能力 %c-%c, '*':一覧, '/'で使用, ESCで中断) どの能力について知りますか?", "(Powers %c-%c, *=List. /=Use, ESC=exit) Browse which power? ");
120 fmt = _("(特殊能力 %c-%c, '*'で一覧, '/'で閲覧, ESCで中断) どの能力を使いますか?", "(Powers %c-%c, *=List, /=Browse, ESC=exit) Use which power? ");
123 (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);
127 * @brief レイシャルパワー選択用のカーソル位置を進める
128 * @param player_ptr プレイヤー情報への参照ポインタ
129 * @param rc_ptr レイシャルパワー情報への参照ポインタ
132 static void racial_power_add_index(PlayerType *player_ptr, rc_type *rc_ptr, int i)
134 auto n = rc_ptr->menu_line + i;
135 if (i < -1 || i > 1) {
136 if (n < 0 || n >= rc_ptr->power_count()) {
141 n = rc_ptr->power_count() - 1;
143 if (n >= rc_ptr->power_count()) {
147 auto p = n / RC_PAGE_SIZE;
148 racial_power_erase_cursor(rc_ptr);
149 rc_ptr->menu_line = n;
150 if (rc_ptr->page != p) {
154 racial_power_display_list(player_ptr, rc_ptr);
156 racial_power_display_cursor(rc_ptr);
161 * @brief メニューによる選択のキーを処理する
162 * @param rc_ptr レイシャルパワー情報への参照ポインタ
163 * @return キャンセルならRC_CANCEL、そうでないならRC_CONTINUE
165 static bool racial_power_interpret_menu_keys(PlayerType *player_ptr, rc_type *rc_ptr)
167 switch (rc_ptr->choice) {
173 racial_power_add_index(player_ptr, rc_ptr, -1);
178 racial_power_add_index(player_ptr, rc_ptr, 1);
183 racial_power_add_index(player_ptr, rc_ptr, RC_PAGE_SIZE);
188 racial_power_add_index(player_ptr, rc_ptr, 0 - RC_PAGE_SIZE);
193 rc_ptr->command_code = (COMMAND_CODE)rc_ptr->menu_line;
194 rc_ptr->is_chosen = true;
198 rc_ptr->browse_mode = !rc_ptr->browse_mode;
199 racial_power_make_prompt(rc_ptr);
207 * @brief メニューからの選択決定を処理
208 * @param player_ptr プレイヤー情報への参照ポインタ
209 * @param rc_ptr レイシャルパワー情報への参照ポインタ
210 * @return キャンセルしたらRC_CANCEL、それ以外ならRC_CONTINUE
212 static bool racial_power_select_by_menu(PlayerType *player_ptr, rc_type *rc_ptr)
214 if (!use_menu || rc_ptr->choice == ' ') {
218 if (racial_power_interpret_menu_keys(player_ptr, rc_ptr)) {
222 if (rc_ptr->menu_line > rc_ptr->power_count()) {
223 rc_ptr->menu_line -= rc_ptr->power_count();
230 * @brief レイシャルパワーの選択を解釈
231 * @param player_ptr プレイヤー情報への参照ポインタ
232 * @param rc_ptr レイシャルパワー情報への参照ポインタ
233 * @return コマンド選択していたらtrue、していなかったらfalse
235 static bool racial_power_interpret_choise(PlayerType *player_ptr, rc_type *rc_ptr)
241 if (rc_ptr->choice == ' ' || rc_ptr->choice == '*') {
243 if (rc_ptr->page > rc_ptr->max_page) {
248 racial_power_display_list(player_ptr, rc_ptr);
252 if (rc_ptr->choice == '/') {
253 rc_ptr->browse_mode = !rc_ptr->browse_mode;
254 racial_power_make_prompt(rc_ptr);
258 if (rc_ptr->choice == '?') {
265 static void decide_racial_command(rc_type *rc_ptr)
271 if (rc_ptr->choice == '\r' && rc_ptr->power_count() == 1) {
272 rc_ptr->choice = 'a';
275 if (!isalpha(rc_ptr->choice)) {
277 rc_ptr->command_code = rc_ptr->choice - '0' + 26;
281 rc_ptr->ask = (isupper(rc_ptr->choice));
283 rc_ptr->choice = (char)tolower(rc_ptr->choice);
286 rc_ptr->command_code = (islower(rc_ptr->choice) ? A2I(rc_ptr->choice) : -1);
289 static bool ask_invoke_racial_power(rc_type *rc_ptr)
291 if ((rc_ptr->command_code < 0) || (rc_ptr->command_code >= rc_ptr->power_count())) {
301 (void)strnfmt(tmp_val, 78, _("%sを使いますか? ", "Use %s? "), rc_ptr->power_desc[rc_ptr->command_code].racial_name.data());
302 return get_check(tmp_val);
305 static void racial_power_display_explanation(PlayerType *player_ptr, rc_type *rc_ptr)
307 auto &rpi = rc_ptr->power_desc[rc_ptr->command_code];
309 term_erase(12, 21, 255);
310 term_erase(12, 20, 255);
311 term_erase(12, 19, 255);
312 term_erase(12, 18, 255);
313 term_erase(12, 17, 255);
314 term_erase(12, 16, 255);
315 display_wrap_around(rpi.text, 62, 17, 15);
317 prt(_("何かキーを押して下さい。", "Hit any key."), 0, 0);
322 racial_power_display_list(player_ptr, rc_ptr);
323 rc_ptr->is_chosen = false;
327 * @brief レイシャルパワー選択処理のメインループ
328 * @param player_ptr プレイヤー情報への参照ポインタ
329 * @param rc_ptr レイシャルパワー情報への参照ポインタ
330 * @return コマンド選択したらRC_CONTINUE、キャンセルしたらRC_CANCEL
332 static bool racial_power_process_input(PlayerType *player_ptr, rc_type *rc_ptr)
334 rc_ptr->choice = (always_show_list || use_menu) ? ESCAPE : 1;
337 if (rc_ptr->choice == ESCAPE) {
338 rc_ptr->choice = ' ';
339 } else if (!get_com(rc_ptr->out_val, &rc_ptr->choice, false)) {
343 if (racial_power_select_by_menu(player_ptr, rc_ptr) == RC_CANCEL) {
347 if (!rc_ptr->is_chosen && racial_power_interpret_choise(player_ptr, rc_ptr)) {
348 decide_racial_command(rc_ptr);
349 if (ask_invoke_racial_power(rc_ptr)) {
350 rc_ptr->is_chosen = true;
354 if (rc_ptr->is_chosen) {
355 if (rc_ptr->browse_mode) {
356 racial_power_display_explanation(player_ptr, rc_ptr);
367 * @brief レイシャル/クラスパワー選択を処理
368 * @param player_ptr プレイヤー情報への参照ポインタ
369 * @param rc_ptr レイシャルパワー情報への参照ポインタ
370 * @return コマンド選択したらRC_CONTINUE、キャンセルしたらRC_CANCEL
372 static bool racial_power_select_power(PlayerType *player_ptr, rc_type *rc_ptr)
374 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 auto canceled = racial_power_process_input(player_ptr, rc_ptr) == RC_CANCEL;
392 repeat_push(rc_ptr->command_code);
397 * @brief レイシャルパワーの使用を試みる
398 * @param player_ptr プレイヤー情報への参照ポインタ
399 * @param rc_ptr レイシャルパワー情報への参照ポインタ
401 * 戻り値の代わりにrc_ptr->castに使用の有無を入れる。
403 static void racial_power_cast_power(PlayerType *player_ptr, rc_type *rc_ptr)
405 auto *rpi_ptr = &rc_ptr->power_desc[rc_ptr->command_code];
407 switch (check_racial_level(player_ptr, rpi_ptr)) {
409 if (rpi_ptr->number < 0) {
410 rc_ptr->cast = exe_racial_power(player_ptr, rpi_ptr->number);
412 rc_ptr->cast = exe_mutation_power(player_ptr, i2enum<PlayerMutationType>(rpi_ptr->number));
419 rc_ptr->cast = false;
425 * @brief レイシャルパワーのコストを減らす
426 * @param player_ptr プレイヤー情報への参照ポインタ
427 * @param rc_ptr レイシャルパワー情報への参照ポインタ
428 * @return コストを減らしたらtrue、減らさなかったらfalse
431 * 戻り値はHP/MPの再描画が必要か判定するのに使用。
433 static bool racial_power_reduce_mana(PlayerType *player_ptr, rc_type *rc_ptr)
435 int racial_cost = rc_ptr->power_desc[rc_ptr->command_code].racial_cost;
436 if (racial_cost == 0) {
440 int actual_racial_cost = racial_cost / 2 + randint1(racial_cost / 2);
442 if (player_ptr->csp >= actual_racial_cost) {
443 player_ptr->csp -= actual_racial_cost;
445 actual_racial_cost -= player_ptr->csp;
447 take_hit(player_ptr, DAMAGE_USELIFE, actual_racial_cost, _("過度の集中", "concentrating too hard"));
454 * @brief レイシャル・パワーコマンドのメインルーチン / Allow user to choose a power (racial / mutation) to activate
455 * @param player_ptr プレイヤーへの参照ポインタ
457 void do_cmd_racial_power(PlayerType *player_ptr)
459 if (player_ptr->wild_mode) {
463 PlayerEnergy energy(player_ptr);
464 if (cmd_limit_confused(player_ptr)) {
465 energy.reset_player_turn();
469 PlayerClass(player_ptr).break_samurai_stance({ SamuraiStanceType::MUSOU, SamuraiStanceType::KOUKIJIN });
471 auto tmp_r = rc_type(player_ptr);
472 auto *rc_ptr = &tmp_r;
474 switch_class_racial(player_ptr, rc_ptr);
476 if (player_ptr->mimic_form != MimicKindType::NONE) {
477 set_mimic_racial_command(player_ptr, rc_ptr);
479 set_race_racial_command(player_ptr, rc_ptr);
482 select_mutation_racial(player_ptr, rc_ptr);
484 if (rc_ptr->power_count() == 0) {
485 msg_print(_("特殊能力はありません。", "You have no special powers."));
489 rc_ptr->max_page = 1 + (rc_ptr->power_count() - 1) / RC_PAGE_SIZE;
490 rc_ptr->page = use_menu ? 0 : -1;
491 racial_power_make_prompt(rc_ptr);
493 if (racial_power_select_power(player_ptr, rc_ptr) == RC_CONTINUE) {
494 racial_power_cast_power(player_ptr, rc_ptr);
498 energy.reset_player_turn();
502 if (!racial_power_reduce_mana(player_ptr, rc_ptr)) {
507 MainWindowRedrawingFlag::HP,
508 MainWindowRedrawingFlag::MP,
510 RedrawingFlagsUpdater::get_instance().set_flags(flags);
511 set_bits(player_ptr->window_flags, PW_PLAYER | PW_SPELL);