3 #include "cmd-action/cmd-racial.h"
4 #include "action/action-limited.h"
5 #include "action/mutation-execution.h"
6 #include "action/racial-execution.h"
7 #include "core/asking-player.h"
8 #include "core/player-redraw-types.h"
9 #include "core/player-update-types.h"
10 #include "core/window-redrawer.h"
11 #include "game-option/text-display-options.h"
12 #include "io/command-repeater.h"
13 #include "io/input-key-acceptor.h"
14 #include "io/input-key-requester.h"
15 #include "main/sound-of-music.h"
16 #include "mutation/mutation-flag-types.h"
17 #include "player-base/player-class.h"
18 #include "player-info/samurai-data-type.h"
19 #include "player-status/player-energy.h"
20 #include "player/attack-defense-types.h"
21 #include "player/player-damage.h"
22 #include "player/special-defense-types.h"
23 #include "racial/class-racial-switcher.h"
24 #include "racial/mutation-racial-selector.h"
25 #include "racial/race-racial-command-setter.h"
26 #include "racial/racial-util.h"
27 #include "status/action-setter.h"
28 #include "system/player-type-definition.h"
29 #include "term/screen-processor.h"
30 #include "util/bit-flags-calculator.h"
31 #include "util/buffer-shaper.h"
32 #include "util/int-char-converter.h"
33 #include "view/display-messages.h"
35 #define RC_PAGE_SIZE 18
37 #define RC_CANCEL true
38 #define RC_CONTINUE false
40 static void racial_power_display_cursor(rc_type *rc_ptr)
42 auto y = rc_ptr->menu_line % RC_PAGE_SIZE;
43 put_str(_(" 》 ", " > "), 2 + y, 11);
46 static void racial_power_erase_cursor(rc_type *rc_ptr)
48 auto y = rc_ptr->menu_line % RC_PAGE_SIZE;
49 put_str(_(" ", " "), 2 + y, 11);
53 * @brief レイシャルパワー一覧を表示
54 * @param player_ptr プレイヤー情報への参照ポインタ
55 * @param rc_ptr レイシャルパワー情報への参照ポインタ
56 * @return キャンセルしたらRC_CANCEL、それ以外ならRC_CONTINUE
58 static void racial_power_display_list(player_type *player_ptr, rc_type *rc_ptr)
64 prt(_(" Lv MP 失率 効果", " Lv MP Fail Effect"), 1, x);
67 for (; y < RC_PAGE_SIZE; y++) {
68 auto ctr = RC_PAGE_SIZE * rc_ptr->page + y;
69 if (ctr >= rc_ptr->power_count())
79 letter = '0' + ctr - 26;
81 sprintf(dummy, " %c) ", letter);
84 auto &rpi = rc_ptr->power_desc[ctr];
86 format("%-30.30s %2d %4d %3d%% %s", rpi.racial_name.c_str(), rpi.min_level, rpi.cost, 100 - racial_chance(player_ptr, &rc_ptr->power_desc[ctr]),
95 racial_power_display_cursor(rc_ptr);
99 * @brief レイシャルパワー選択用のプロンプトを作成する
100 * @param rc_ptr レイシャルパワー情報への参照ポインタ
102 static void racial_power_make_prompt(rc_type *rc_ptr)
106 if (rc_ptr->browse_mode)
108 "(特殊能力 %c-%c, '*':一覧, '/'で使用, ESCで中断) どの能力について知りますか?", "(Powers %c-%c, *=List. /=Use, ESC=exit) Browse which power? ");
110 fmt = _("(特殊能力 %c-%c, '*'で一覧, '/'で閲覧, ESCで中断) どの能力を使いますか?", "(Powers %c-%c, *=List, /=Browse, ESC=exit) Use which power? ");
112 (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);
116 * @brief レイシャルパワー選択用のカーソル位置を進める
117 * @param player_ptr プレイヤー情報への参照ポインタ
118 * @param rc_ptr レイシャルパワー情報への参照ポインタ
121 static void racial_power_add_index(player_type *player_ptr, rc_type *rc_ptr, int i)
123 auto n = rc_ptr->menu_line + i;
124 if (i < -1 || i > 1) {
125 if (n < 0 || n >= rc_ptr->power_count())
129 n = rc_ptr->power_count() - 1;
130 if (n >= rc_ptr->power_count())
133 auto p = n / RC_PAGE_SIZE;
134 racial_power_erase_cursor(rc_ptr);
135 rc_ptr->menu_line = n;
136 if (rc_ptr->page != p) {
140 racial_power_display_list(player_ptr, rc_ptr);
142 racial_power_display_cursor(rc_ptr);
146 * @brief メニューによる選択のキーを処理する
147 * @param rc_ptr レイシャルパワー情報への参照ポインタ
148 * @return キャンセルならRC_CANCEL、そうでないならRC_CONTINUE
150 static bool racial_power_interpret_menu_keys(player_type *player_ptr, rc_type *rc_ptr)
152 switch (rc_ptr->choice) {
158 racial_power_add_index(player_ptr, rc_ptr, -1);
163 racial_power_add_index(player_ptr, rc_ptr, 1);
168 racial_power_add_index(player_ptr, rc_ptr, RC_PAGE_SIZE);
173 racial_power_add_index(player_ptr, rc_ptr, 0 - RC_PAGE_SIZE);
178 rc_ptr->command_code = (COMMAND_CODE)rc_ptr->menu_line;
179 rc_ptr->is_chosen = true;
183 rc_ptr->browse_mode = !rc_ptr->browse_mode;
184 racial_power_make_prompt(rc_ptr);
192 * @brief メニューからの選択決定を処理
193 * @param player_ptr プレイヤー情報への参照ポインタ
194 * @param rc_ptr レイシャルパワー情報への参照ポインタ
195 * @return キャンセルしたらRC_CANCEL、それ以外ならRC_CONTINUE
197 static bool racial_power_select_by_menu(player_type *player_ptr, rc_type *rc_ptr)
199 if (!use_menu || rc_ptr->choice == ' ')
202 if (racial_power_interpret_menu_keys(player_ptr, rc_ptr))
205 if (rc_ptr->menu_line > rc_ptr->power_count())
206 rc_ptr->menu_line -= rc_ptr->power_count();
212 * @brief レイシャルパワーの選択を解釈
213 * @param player_ptr プレイヤー情報への参照ポインタ
214 * @param rc_ptr レイシャルパワー情報への参照ポインタ
215 * @return コマンド選択していたらtrue、していなかったらfalse
217 static bool racial_power_interpret_choise(player_type *player_ptr, rc_type *rc_ptr)
222 if (rc_ptr->choice == ' ' || rc_ptr->choice == '*') {
224 if (rc_ptr->page > rc_ptr->max_page)
228 racial_power_display_list(player_ptr, rc_ptr);
232 if (rc_ptr->choice == '/') {
233 rc_ptr->browse_mode = !rc_ptr->browse_mode;
234 racial_power_make_prompt(rc_ptr);
238 if (rc_ptr->choice == '?')
244 static void decide_racial_command(rc_type *rc_ptr)
249 if (rc_ptr->choice == '\r' && rc_ptr->power_count() == 1)
250 rc_ptr->choice = 'a';
252 if (!isalpha(rc_ptr->choice)) {
254 rc_ptr->command_code = rc_ptr->choice - '0' + 26;
258 rc_ptr->ask = (isupper(rc_ptr->choice));
260 rc_ptr->choice = (char)tolower(rc_ptr->choice);
262 rc_ptr->command_code = (islower(rc_ptr->choice) ? A2I(rc_ptr->choice) : -1);
265 static bool ask_invoke_racial_power(rc_type *rc_ptr)
267 if ((rc_ptr->command_code < 0) || (rc_ptr->command_code >= rc_ptr->power_count())) {
276 (void)strnfmt(tmp_val, 78, _("%sを使いますか? ", "Use %s? "), rc_ptr->power_desc[rc_ptr->command_code].racial_name.c_str());
277 return get_check(tmp_val);
280 static void racial_power_display_explanation(player_type *player_ptr, rc_type *rc_ptr)
282 auto &rpi = rc_ptr->power_desc[rc_ptr->command_code];
285 term_erase(12, 21, 255);
286 term_erase(12, 20, 255);
287 term_erase(12, 19, 255);
288 term_erase(12, 18, 255);
289 term_erase(12, 17, 255);
290 term_erase(12, 16, 255);
291 shape_buffer(rpi.text.c_str(), 62, temp, sizeof(temp));
292 for (int j = 0, line = 17; temp[j]; j += (1 + strlen(&temp[j]))) {
293 prt(&temp[j], line, 15);
297 prt(_("何かキーを押して下さい。", "Hit any key."), 0, 0);
302 racial_power_display_list(player_ptr, rc_ptr);
303 rc_ptr->is_chosen = false;
307 * @brief レイシャルパワー選択処理のメインループ
308 * @param player_ptr プレイヤー情報への参照ポインタ
309 * @param rc_ptr レイシャルパワー情報への参照ポインタ
310 * @return コマンド選択したらRC_CONTINUE、キャンセルしたらRC_CANCEL
312 static bool racial_power_process_input(player_type *player_ptr, rc_type *rc_ptr)
314 rc_ptr->choice = (always_show_list || use_menu) ? ESCAPE : 1;
317 if (rc_ptr->choice == ESCAPE)
318 rc_ptr->choice = ' ';
319 else if (!get_com(rc_ptr->out_val, &rc_ptr->choice, false))
322 if (racial_power_select_by_menu(player_ptr, rc_ptr) == RC_CANCEL)
325 if (!rc_ptr->is_chosen && racial_power_interpret_choise(player_ptr, rc_ptr)) {
326 decide_racial_command(rc_ptr);
327 if (ask_invoke_racial_power(rc_ptr))
328 rc_ptr->is_chosen = true;
331 if (rc_ptr->is_chosen) {
332 if (rc_ptr->browse_mode)
333 racial_power_display_explanation(player_ptr, rc_ptr);
343 * @brief レイシャル/クラスパワー選択を処理
344 * @param player_ptr プレイヤー情報への参照ポインタ
345 * @param rc_ptr レイシャルパワー情報への参照ポインタ
346 * @return コマンド選択したらRC_CONTINUE、キャンセルしたらRC_CANCEL
348 static bool racial_power_select_power(player_type *player_ptr, rc_type *rc_ptr)
350 if (repeat_pull(&rc_ptr->command_code) && rc_ptr->command_code >= 0 && rc_ptr->command_code < rc_ptr->power_count())
356 racial_power_display_list(player_ptr, rc_ptr);
358 auto canceled = racial_power_process_input(player_ptr, rc_ptr) == RC_CANCEL;
365 repeat_push(rc_ptr->command_code);
370 * @brief レイシャルパワーの使用を試みる
371 * @param player_ptr プレイヤー情報への参照ポインタ
372 * @param rc_ptr レイシャルパワー情報への参照ポインタ
374 * 戻り値の代わりにrc_ptr->castに使用の有無を入れる。
376 static void racial_power_cast_power(player_type *player_ptr, rc_type *rc_ptr)
378 auto *rpi_ptr = &rc_ptr->power_desc[rc_ptr->command_code];
380 switch (check_racial_level(player_ptr, rpi_ptr)) {
382 if (rpi_ptr->number < 0)
383 rc_ptr->cast = exe_racial_power(player_ptr, rpi_ptr->number);
385 rc_ptr->cast = exe_mutation_power(player_ptr, i2enum<MUTA>(rpi_ptr->number));
391 rc_ptr->cast = false;
397 * @brief レイシャルパワーのコストを減らす
398 * @param player_ptr プレイヤー情報への参照ポインタ
399 * @param rc_ptr レイシャルパワー情報への参照ポインタ
400 * @return コストを減らしたらtrue、減らさなかったらfalse
403 * 戻り値はHP/MPの再描画が必要か判定するのに使用。
405 static bool racial_power_reduce_mana(player_type *player_ptr, rc_type *rc_ptr)
407 int racial_cost = rc_ptr->power_desc[rc_ptr->command_code].racial_cost;
408 if (racial_cost == 0)
411 int actual_racial_cost = racial_cost / 2 + randint1(racial_cost / 2);
413 if (player_ptr->csp >= actual_racial_cost)
414 player_ptr->csp -= actual_racial_cost;
416 actual_racial_cost -= player_ptr->csp;
418 take_hit(player_ptr, DAMAGE_USELIFE, actual_racial_cost, _("過度の集中", "concentrating too hard"));
425 * @brief レイシャル・パワーコマンドのメインルーチン / Allow user to choose a power (racial / mutation) to activate
426 * @param player_ptr プレイヤーへの参照ポインタ
428 void do_cmd_racial_power(player_type *player_ptr)
430 if (player_ptr->wild_mode)
433 PlayerEnergy energy(player_ptr);
434 if (cmd_limit_confused(player_ptr))
436 energy.reset_player_turn();
440 PlayerClass(player_ptr).break_samurai_stance({ SamuraiStance::MUSOU, SamuraiStance::KOUKIJIN });
442 auto tmp_r = rc_type(player_ptr);
443 auto *rc_ptr = &tmp_r;
445 switch_class_racial(player_ptr, rc_ptr);
447 if (player_ptr->mimic_form)
448 set_mimic_racial_command(player_ptr, rc_ptr);
450 set_race_racial_command(player_ptr, rc_ptr);
452 select_mutation_racial(player_ptr, rc_ptr);
454 if (rc_ptr->power_count() == 0) {
455 msg_print(_("特殊能力はありません。", "You have no special powers."));
459 rc_ptr->max_page = 1 + (rc_ptr->power_count() - 1) / RC_PAGE_SIZE;
460 rc_ptr->page = use_menu ? 0 : -1;
461 racial_power_make_prompt(rc_ptr);
463 if (racial_power_select_power(player_ptr, rc_ptr) == RC_CONTINUE)
464 racial_power_cast_power(player_ptr, rc_ptr);
467 energy.reset_player_turn();
471 if (!racial_power_reduce_mana(player_ptr, rc_ptr))
474 set_bits(player_ptr->redraw, PR_HP | PR_MANA);
475 set_bits(player_ptr->window_flags, PW_PLAYER | PW_SPELL);