1 #include "window/main-window-stat-poster.h"
2 #include "game-option/game-play-options.h"
3 #include "io/input-key-requester.h"
4 #include "mind/stances-table.h"
5 #include "monster/monster-status.h"
6 #include "player-base/player-class.h"
7 #include "player-info/bluemage-data-type.h"
8 #include "player-info/mane-data-type.h"
9 #include "player-info/monk-data-type.h"
10 #include "player-info/ninja-data-type.h"
11 #include "player-info/samurai-data-type.h"
12 #include "player-info/sniper-data-type.h"
13 #include "player/attack-defense-types.h"
14 #include "player/digestion-processor.h"
15 #include "player/player-status-table.h"
16 #include "player/player-status.h"
17 #include "realm/realm-hex-numbers.h"
18 #include "realm/realm-types.h"
19 #include "spell-realm/spells-hex.h"
20 #include "status/element-resistance.h"
21 #include "system/floor-type-definition.h"
22 #include "system/monster-type-definition.h"
23 #include "system/player-type-definition.h"
24 #include "term/screen-processor.h"
25 #include "term/term-color-types.h"
26 #include "timed-effect/player-cut.h"
27 #include "timed-effect/player-stun.h"
28 #include "timed-effect/timed-effects.h"
29 #include "view/status-bars-table.h"
30 #include "window/main-window-row-column.h"
33 * @brief 32ビット変数配列の指定位置のビットフラグを1にする。
34 * @param FLG フラグ位置(ビット)
36 #define ADD_BAR_FLAG(FLG) (bar_flags[FLG / 32] |= (1UL << (FLG % 32)))
39 * @brief 32ビット変数配列の指定位置のビットフラグが1かどうかを返す。
40 * @param FLG フラグ位置(ビット)
43 #define IS_BAR_FLAG(FLG) (bar_flags[FLG / 32] & (1UL << (FLG % 32)))
46 * @brief プレイヤー能力値を描画する / Print character stat in given row, column
47 * @param stat 描画するステータスのID
49 void print_stat(PlayerType *player_ptr, int stat)
52 if (player_ptr->stat_cur[stat] < player_ptr->stat_max[stat]) {
53 put_str(stat_names_reduced[stat], ROW_STAT + stat, 0);
54 cnv_stat(player_ptr->stat_use[stat], tmp);
55 c_put_str(TERM_YELLOW, tmp, ROW_STAT + stat, COL_STAT + 6);
57 put_str(stat_names[stat], ROW_STAT + stat, 0);
58 cnv_stat(player_ptr->stat_use[stat], tmp);
59 c_put_str(TERM_L_GREEN, tmp, ROW_STAT + stat, COL_STAT + 6);
62 if (player_ptr->stat_max[stat] != player_ptr->stat_max_max[stat])
66 /* 日本語にかぶらないように表示位置を変更 */
67 put_str("!", ROW_STAT + stat, 5);
69 put_str("!", ROW_STAT + stat, 3);
74 * @brief プレイヤーの負傷状態を表示する
76 void print_cut(PlayerType *player_ptr)
78 auto player_cut = player_ptr->effects()->cut();
79 if (!player_cut->is_cut()) {
80 put_str(" ", ROW_CUT, COL_CUT);
84 auto [color, stat] = player_cut->get_expr();
85 c_put_str(color, stat.data(), ROW_CUT, COL_CUT);
89 * @brief プレイヤーの朦朧状態を表示する
90 * @param player_ptr プレイヤーへの参照ポインタ
92 void print_stun(PlayerType *player_ptr)
94 auto player_stun = player_ptr->effects()->stun();
95 if (!player_stun->is_stunned()) {
96 put_str(" ", ROW_STUN, COL_STUN);
100 auto [color, stat] = player_stun->get_expr();
101 c_put_str(color, stat.data(), ROW_STUN, COL_STUN);
105 * @brief プレイヤーの空腹状態を表示する / Prints status of hunger
106 * @param player_ptr プレイヤーへの参照ポインタ
108 void print_hunger(PlayerType *player_ptr)
110 if (allow_debug_options && player_ptr->current_floor_ptr->inside_arena)
113 if (player_ptr->food < PY_FOOD_FAINT) {
114 c_put_str(TERM_RED, _("衰弱 ", "Weak "), ROW_HUNGRY, COL_HUNGRY);
118 if (player_ptr->food < PY_FOOD_WEAK) {
119 c_put_str(TERM_ORANGE, _("衰弱 ", "Weak "), ROW_HUNGRY, COL_HUNGRY);
123 if (player_ptr->food < PY_FOOD_ALERT) {
124 c_put_str(TERM_YELLOW, _("空腹 ", "Hungry"), ROW_HUNGRY, COL_HUNGRY);
128 if (player_ptr->food < PY_FOOD_FULL) {
129 c_put_str(TERM_L_GREEN, " ", ROW_HUNGRY, COL_HUNGRY);
133 if (player_ptr->food < PY_FOOD_MAX) {
134 c_put_str(TERM_L_GREEN, _("満腹 ", "Full "), ROW_HUNGRY, COL_HUNGRY);
138 c_put_str(TERM_GREEN, _("食過ぎ", "Gorged"), ROW_HUNGRY, COL_HUNGRY);
142 * @brief プレイヤーの行動状態を表示する / Prints Searching, Resting, Paralysis, or 'count' status
143 * @param player_ptr プレイヤーへの参照ポインタ
145 * Display is always exactly 10 characters wide (see below)
146 * This function was a major bottleneck when resting, so a lot of
147 * the text formatting code was optimized in place below.
149 void print_state(PlayerType *player_ptr)
151 TERM_COLOR attr = TERM_WHITE;
154 if (command_rep > 999) {
155 (void)sprintf(text, "%2d00", command_rep / 100);
157 (void)sprintf(text, " %2d", command_rep);
160 c_put_str(attr, format("%5.5s", text), ROW_STATE, COL_STATE);
164 switch (player_ptr->action) {
165 case ACTION_SEARCH: {
166 strcpy(text, _("探索", "Sear"));
170 strcpy(text, _(" ", " "));
171 if (player_ptr->resting > 0) {
172 sprintf(text, "%4d", player_ptr->resting);
173 } else if (player_ptr->resting == COMMAND_ARG_REST_FULL_HEALING) {
174 text[0] = text[1] = text[2] = text[3] = '*';
175 } else if (player_ptr->resting == COMMAND_ARG_REST_UNTIL_DONE) {
176 text[0] = text[1] = text[2] = text[3] = '&';
182 strcpy(text, _("学習", "lear"));
183 auto bluemage_data = PlayerClass(player_ptr).get_specific_data<bluemage_data_type>();
184 if (bluemage_data->new_magic_learned)
189 strcpy(text, _("釣り", "fish"));
192 case ACTION_MONK_STANCE: {
193 if (auto stance = PlayerClass(player_ptr).get_monk_stance();
194 stance != MonkStanceType::NONE) {
196 case MonkStanceType::GENBU:
199 case MonkStanceType::BYAKKO:
202 case MonkStanceType::SEIRYU:
205 case MonkStanceType::SUZAKU:
211 strcpy(text, monk_stances[enum2i(stance) - 1].desc);
215 case ACTION_SAMURAI_STANCE: {
216 if (auto stance = PlayerClass(player_ptr).get_samurai_stance();
217 stance != SamuraiStanceType::NONE) {
218 strcpy(text, samurai_stances[enum2i(stance) - 1].desc);
223 strcpy(text, _("歌 ", "Sing"));
226 case ACTION_HAYAGAKE: {
227 strcpy(text, _("速駆", "Fast"));
231 strcpy(text, _("詠唱", "Spel"));
240 c_put_str(attr, format("%5.5s", text), ROW_STATE, COL_STATE);
244 * @brief プレイヤーの行動速度を表示する / Prints the speed_value of a character. -CJS-
245 * @param player_ptr プレイヤーへの参照ポインタ
247 void print_speed(PlayerType *player_ptr)
250 term_get_size(&wid, &hgt);
251 TERM_LEN col_speed = wid + COL_SPEED;
252 TERM_LEN row_speed = hgt + ROW_SPEED;
254 int speed_value = player_ptr->pspeed - 110;
256 floor_type *floor_ptr = player_ptr->current_floor_ptr;
257 bool is_player_fast = is_fast(player_ptr);
259 TERM_COLOR attr = TERM_WHITE;
260 if (speed_value > 0) {
261 if (player_ptr->riding) {
262 monster_type *m_ptr = &floor_ptr->m_list[player_ptr->riding];
263 if (monster_fast_remaining(m_ptr) && !monster_slow_remaining(m_ptr))
265 else if (monster_slow_remaining(m_ptr) && !monster_fast_remaining(m_ptr))
269 } else if ((is_player_fast && !player_ptr->slow) || player_ptr->lightspeed)
271 else if (player_ptr->slow && !is_player_fast)
275 sprintf(buf, "%s(+%d)", (player_ptr->riding ? _("乗馬", "Ride") : _("加速", "Fast")), speed_value);
276 } else if (speed_value < 0) {
277 if (player_ptr->riding) {
278 monster_type *m_ptr = &floor_ptr->m_list[player_ptr->riding];
279 if (monster_fast_remaining(m_ptr) && !monster_slow_remaining(m_ptr))
281 else if (monster_slow_remaining(m_ptr) && !monster_fast_remaining(m_ptr))
285 } else if (is_player_fast && !player_ptr->slow)
287 else if (player_ptr->slow && !is_player_fast)
291 sprintf(buf, "%s(%d)", (player_ptr->riding ? _("乗馬", "Ride") : _("減速", "Slow")), speed_value);
292 } else if (player_ptr->riding) {
294 strcpy(buf, _("乗馬中", "Riding"));
297 c_put_str(attr, format("%-9s", buf), row_speed, col_speed);
301 * @brief プレイヤーの呪文学習可能状態を表示する
302 * @param player_ptr プレイヤーへの参照ポインタ
304 void print_study(PlayerType *player_ptr)
307 term_get_size(&wid, &hgt);
308 TERM_LEN col_study = wid + COL_STUDY;
309 TERM_LEN row_study = hgt + ROW_STUDY;
311 if (player_ptr->new_spells) {
312 put_str(_("学習", "Stud"), row_study, col_study);
314 put_str(" ", row_study, col_study);
319 * @brief プレイヤーのものまね可能状態を表示する
320 * @param player_ptr プレイヤーへの参照ポインタ
322 void print_imitation(PlayerType *player_ptr)
325 term_get_size(&wid, &hgt);
326 TERM_LEN col_study = wid + COL_STUDY;
327 TERM_LEN row_study = hgt + ROW_STUDY;
329 if (player_ptr->pclass != PlayerClassType::IMITATOR)
332 auto mane_data = PlayerClass(player_ptr).get_specific_data<mane_data_type>();
334 if (mane_data->mane_list.size() == 0) {
335 put_str(" ", row_study, col_study);
339 TERM_COLOR attr = mane_data->new_mane ? TERM_L_RED : TERM_WHITE;
340 c_put_str(attr, _("まね", "Imit"), row_study, col_study);
344 * @brief 画面下部に表示すべき呪術の呪文をリストアップする
345 * @param player_ptr プレイヤーへの参照ポインタ
346 * @bar_flags 表示可否を決めるためのフラグ群
348 static void add_hex_status_flags(PlayerType *player_ptr, BIT_FLAGS *bar_flags)
350 if (player_ptr->realm1 != REALM_HEX) {
354 SpellHex spell_hex(player_ptr);
355 if (spell_hex.is_spelling_specific(HEX_BLESS)) {
356 ADD_BAR_FLAG(BAR_BLESSED);
359 if (spell_hex.is_spelling_specific(HEX_DEMON_AURA)) {
360 ADD_BAR_FLAG(BAR_SHFIRE);
361 ADD_BAR_FLAG(BAR_REGENERATION);
364 if (spell_hex.is_spelling_specific(HEX_XTRA_MIGHT)) {
365 ADD_BAR_FLAG(BAR_MIGHT);
368 if (spell_hex.is_spelling_specific(HEX_DETECT_EVIL)) {
369 ADD_BAR_FLAG(BAR_ESP_EVIL);
372 if (spell_hex.is_spelling_specific(HEX_ICE_ARMOR)) {
373 ADD_BAR_FLAG(BAR_SHCOLD);
376 if (spell_hex.is_spelling_specific(HEX_RUNESWORD)) {
377 ADD_BAR_FLAG(BAR_RUNESWORD);
380 if (spell_hex.is_spelling_specific(HEX_BUILDING)) {
381 ADD_BAR_FLAG(BAR_BUILD);
384 if (spell_hex.is_spelling_specific(HEX_ANTI_TELE)) {
385 ADD_BAR_FLAG(BAR_ANTITELE);
388 if (spell_hex.is_spelling_specific(HEX_SHOCK_CLOAK)) {
389 ADD_BAR_FLAG(BAR_SHELEC);
392 if (spell_hex.is_spelling_specific(HEX_SHADOW_CLOAK)) {
393 ADD_BAR_FLAG(BAR_SHSHADOW);
396 if (spell_hex.is_spelling_specific(HEX_CONFUSION)) {
397 ADD_BAR_FLAG(BAR_ATTKCONF);
400 if (spell_hex.is_spelling_specific(HEX_EYE_FOR_EYE)) {
401 ADD_BAR_FLAG(BAR_EYEEYE);
404 if (spell_hex.is_spelling_specific(HEX_ANTI_MULTI)) {
405 ADD_BAR_FLAG(BAR_ANTIMULTI);
408 if (spell_hex.is_spelling_specific(HEX_VAMP_BLADE)) {
409 ADD_BAR_FLAG(BAR_VAMPILIC);
412 if (spell_hex.is_spelling_specific(HEX_ANTI_MAGIC)) {
413 ADD_BAR_FLAG(BAR_ANTIMAGIC);
416 if (spell_hex.is_spelling_specific(HEX_CURE_LIGHT) || spell_hex.is_spelling_specific(HEX_CURE_SERIOUS)
417 || spell_hex.is_spelling_specific(HEX_CURE_CRITICAL)) {
418 ADD_BAR_FLAG(BAR_CURE);
421 if (spell_hex.get_revenge_turn() > 0) {
422 auto revenge_type = spell_hex.get_revenge_type();
423 if (revenge_type == SpellHexRevengeType::PATIENCE) {
424 ADD_BAR_FLAG(BAR_PATIENCE);
427 if (revenge_type == SpellHexRevengeType::REVENGE) {
428 ADD_BAR_FLAG(BAR_REVENGE);
434 * @brief 下部に状態表示を行う / Show status bar
436 void print_status(PlayerType *player_ptr)
439 term_get_size(&wid, &hgt);
440 TERM_LEN row_statbar = hgt + ROW_STATBAR;
441 TERM_LEN max_col_statbar = wid + MAX_COL_STATBAR;
443 term_erase(0, row_statbar, max_col_statbar);
445 BIT_FLAGS bar_flags[3];
446 bar_flags[0] = bar_flags[1] = bar_flags[2] = 0L;
448 if (player_ptr->tsuyoshi)
449 ADD_BAR_FLAG(BAR_TSUYOSHI);
451 if (player_ptr->hallucinated)
452 ADD_BAR_FLAG(BAR_HALLUCINATION);
454 if (player_ptr->blind)
455 ADD_BAR_FLAG(BAR_BLINDNESS);
457 if (player_ptr->paralyzed)
458 ADD_BAR_FLAG(BAR_PARALYZE);
460 if (player_ptr->confused)
461 ADD_BAR_FLAG(BAR_CONFUSE);
463 if (player_ptr->poisoned)
464 ADD_BAR_FLAG(BAR_POISONED);
466 if (player_ptr->tim_invis)
467 ADD_BAR_FLAG(BAR_SENSEUNSEEN);
469 auto sniper_data = PlayerClass(player_ptr).get_specific_data<sniper_data_type>();
470 if (sniper_data && (sniper_data->concent >= CONCENT_RADAR_THRESHOLD)) {
471 ADD_BAR_FLAG(BAR_SENSEUNSEEN);
472 ADD_BAR_FLAG(BAR_NIGHTSIGHT);
475 if (is_time_limit_esp(player_ptr))
476 ADD_BAR_FLAG(BAR_TELEPATHY);
478 if (player_ptr->tim_regen)
479 ADD_BAR_FLAG(BAR_REGENERATION);
481 if (player_ptr->tim_infra)
482 ADD_BAR_FLAG(BAR_INFRAVISION);
484 if (player_ptr->protevil)
485 ADD_BAR_FLAG(BAR_PROTEVIL);
487 if (is_invuln(player_ptr))
488 ADD_BAR_FLAG(BAR_INVULN);
490 if (player_ptr->wraith_form)
491 ADD_BAR_FLAG(BAR_WRAITH);
493 if (player_ptr->tim_pass_wall)
494 ADD_BAR_FLAG(BAR_PASSWALL);
496 if (player_ptr->tim_reflect)
497 ADD_BAR_FLAG(BAR_REFLECTION);
499 if (is_hero(player_ptr))
500 ADD_BAR_FLAG(BAR_HEROISM);
502 if (is_shero(player_ptr))
503 ADD_BAR_FLAG(BAR_BERSERK);
505 if (is_blessed(player_ptr))
506 ADD_BAR_FLAG(BAR_BLESSED);
508 if (player_ptr->magicdef)
509 ADD_BAR_FLAG(BAR_MAGICDEFENSE);
511 if (player_ptr->tsubureru)
512 ADD_BAR_FLAG(BAR_EXPAND);
514 if (player_ptr->shield)
515 ADD_BAR_FLAG(BAR_STONESKIN);
517 auto ninja_data = PlayerClass(player_ptr).get_specific_data<ninja_data_type>();
518 if (ninja_data && ninja_data->kawarimi)
519 ADD_BAR_FLAG(BAR_KAWARIMI);
521 if (player_ptr->special_defense & DEFENSE_ACID)
522 ADD_BAR_FLAG(BAR_IMMACID);
523 if (is_oppose_acid(player_ptr))
524 ADD_BAR_FLAG(BAR_RESACID);
526 if (player_ptr->special_defense & DEFENSE_ELEC)
527 ADD_BAR_FLAG(BAR_IMMELEC);
528 if (is_oppose_elec(player_ptr))
529 ADD_BAR_FLAG(BAR_RESELEC);
531 if (player_ptr->special_defense & DEFENSE_FIRE)
532 ADD_BAR_FLAG(BAR_IMMFIRE);
533 if (is_oppose_fire(player_ptr))
534 ADD_BAR_FLAG(BAR_RESFIRE);
536 if (player_ptr->special_defense & DEFENSE_COLD)
537 ADD_BAR_FLAG(BAR_IMMCOLD);
538 if (is_oppose_cold(player_ptr))
539 ADD_BAR_FLAG(BAR_RESCOLD);
541 if (is_oppose_pois(player_ptr))
542 ADD_BAR_FLAG(BAR_RESPOIS);
544 if (player_ptr->word_recall)
545 ADD_BAR_FLAG(BAR_RECALL);
547 if (player_ptr->alter_reality)
548 ADD_BAR_FLAG(BAR_ALTER);
550 if (player_ptr->afraid)
551 ADD_BAR_FLAG(BAR_AFRAID);
553 if (player_ptr->tim_res_time)
554 ADD_BAR_FLAG(BAR_RESTIME);
556 if (player_ptr->multishadow)
557 ADD_BAR_FLAG(BAR_MULTISHADOW);
559 if (player_ptr->special_attack & ATTACK_CONFUSE)
560 ADD_BAR_FLAG(BAR_ATTKCONF);
562 if (player_ptr->resist_magic)
563 ADD_BAR_FLAG(BAR_REGMAGIC);
565 if (player_ptr->ult_res)
566 ADD_BAR_FLAG(BAR_ULTIMATE);
568 if (player_ptr->tim_levitation)
569 ADD_BAR_FLAG(BAR_LEVITATE);
571 if (player_ptr->tim_res_nether)
572 ADD_BAR_FLAG(BAR_RESNETH);
574 if (player_ptr->dustrobe)
575 ADD_BAR_FLAG(BAR_DUSTROBE);
577 if (player_ptr->special_attack & ATTACK_FIRE)
578 ADD_BAR_FLAG(BAR_ATTKFIRE);
579 if (player_ptr->special_attack & ATTACK_COLD)
580 ADD_BAR_FLAG(BAR_ATTKCOLD);
581 if (player_ptr->special_attack & ATTACK_ELEC)
582 ADD_BAR_FLAG(BAR_ATTKELEC);
583 if (player_ptr->special_attack & ATTACK_ACID)
584 ADD_BAR_FLAG(BAR_ATTKACID);
585 if (player_ptr->special_attack & ATTACK_POIS)
586 ADD_BAR_FLAG(BAR_ATTKPOIS);
587 if (ninja_data && ninja_data->s_stealth)
588 ADD_BAR_FLAG(BAR_SUPERSTEALTH);
590 if (player_ptr->tim_sh_fire)
591 ADD_BAR_FLAG(BAR_SHFIRE);
593 if (is_time_limit_stealth(player_ptr))
594 ADD_BAR_FLAG(BAR_STEALTH);
596 if (player_ptr->tim_sh_touki)
597 ADD_BAR_FLAG(BAR_TOUKI);
599 if (player_ptr->tim_sh_holy)
600 ADD_BAR_FLAG(BAR_SHHOLY);
602 if (player_ptr->tim_eyeeye)
603 ADD_BAR_FLAG(BAR_EYEEYE);
605 add_hex_status_flags(player_ptr, bar_flags);
606 TERM_LEN col = 0, num = 0;
607 for (int i = 0; stat_bars[i].sstr; i++) {
608 if (IS_BAR_FLAG(i)) {
609 col += strlen(stat_bars[i].lstr) + 1;
615 if (col - 1 > max_col_statbar) {
619 for (int i = 0; stat_bars[i].sstr; i++) {
620 if (IS_BAR_FLAG(i)) {
621 col += strlen(stat_bars[i].sstr);
625 if (col - 1 <= max_col_statbar - (num - 1)) {
631 col = (max_col_statbar - col) / 2;
632 for (int i = 0; stat_bars[i].sstr; i++) {
638 str = stat_bars[i].lstr;
640 str = stat_bars[i].sstr;
642 c_put_str(stat_bars[i].attr, str, row_statbar, col);
646 if (col > max_col_statbar)
652 * @brief プレイヤーのステータスを一括表示する(下部分) / Display extra info (mostly below map)
653 * @param player_ptr プレイヤーへの参照ポインタ
655 void print_frame_extra(PlayerType *player_ptr)
657 print_cut(player_ptr);
658 print_stun(player_ptr);
659 print_hunger(player_ptr);
660 print_state(player_ptr);
661 print_speed(player_ptr);
662 print_study(player_ptr);
663 print_imitation(player_ptr);
664 print_status(player_ptr);