2 * @brief プレイヤーのステータス表示メインルーチン群
6 * ここにこれ以上関数を引っ越してくるのは禁止
9 #include "view/display-player.h"
10 #include "dungeon/quest.h"
11 #include "floor/floor-util.h"
12 #include "game-option/text-display-options.h"
13 #include "info-reader/fixed-map-parser.h"
14 #include "inventory/inventory-slot-types.h"
15 #include "knowledge/knowledge-mutations.h"
16 #include "mind/mind-elementalist.h"
17 #include "mutation/mutation-flag-types.h"
18 #include "object/object-info.h"
19 #include "player-base/player-class.h"
20 #include "player-info/alignment.h"
21 #include "player-info/class-info.h"
22 #include "player-info/mimic-info-table.h"
23 #include "player/patron.h"
24 #include "player/player-personality.h"
25 #include "player/player-sex.h"
26 #include "player/player-status-flags.h"
27 #include "player/player-status-table.h"
28 #include "player/player-status.h"
29 #include "realm/realm-names-table.h"
30 #include "system/baseitem-info.h"
31 #include "system/floor-type-definition.h"
32 #include "system/item-entity.h"
33 #include "system/player-type-definition.h"
34 #include "term/gameterm.h"
35 #include "term/screen-processor.h"
36 #include "term/term-color-types.h"
37 #include "view/display-characteristic.h"
38 #include "view/display-player-middle.h"
39 #include "view/display-player-misc-info.h"
40 #include "view/display-player-stat-info.h"
41 #include "view/display-util.h"
42 #include "view/status-first-page.h"
43 #include "world/world.h"
48 * @param player_ptr プレイヤーへの参照ポインタ
49 * @param mode ステータス表示モード
50 * @return どれかの処理をこなしたらTRUE、何もしなかったらFALSE
52 static bool display_player_info(PlayerType *player_ptr, int mode)
55 display_player_misc_info(player_ptr);
56 display_player_stat_info(player_ptr);
57 display_player_flag_info_1(player_ptr, display_player_equippy);
62 display_player_flag_info_2(player_ptr, display_player_equippy);
67 display_player_flag_info_3(player_ptr, display_player_equippy);
72 TermCenteredOffsetSetter tcos(MAIN_TERM_MIN_COLS, std::nullopt);
73 do_cmd_knowledge_mutations(player_ptr);
81 * @brief 名前、性別、種族、職業を表示する
82 * @param player_ptr プレイヤーへの参照ポインタ
84 static void display_player_basic_info(PlayerType *player_ptr)
86 std::string tmp = ap_ptr->title;
87 tmp.append(_(ap_ptr->no == 1 ? "の" : "", " ")).append(player_ptr->name);
89 display_player_one_line(ENTRY_NAME, tmp, TERM_L_BLUE);
90 display_player_one_line(ENTRY_SEX, sp_ptr->title, TERM_L_BLUE);
91 display_player_one_line(ENTRY_RACE, (player_ptr->mimic_form != MimicKindType::NONE ? mimic_info.at(player_ptr->mimic_form).title : rp_ptr->title), TERM_L_BLUE);
92 display_player_one_line(ENTRY_CLASS, cp_ptr->title, TERM_L_BLUE);
97 * @param player_ptr プレイヤーへの参照ポインタ
99 static void display_magic_realms(PlayerType *player_ptr)
101 if (player_ptr->realm1 == REALM_NONE && player_ptr->element == REALM_NONE) {
106 if (PlayerClass(player_ptr).equals(PlayerClassType::ELEMENTALIST)) {
107 tmp = get_element_title(player_ptr->element);
108 } else if (player_ptr->realm2) {
109 tmp = realm_names[player_ptr->realm1];
110 tmp.append(", ").append(realm_names[player_ptr->realm2]);
112 tmp = realm_names[player_ptr->realm1];
115 display_player_one_line(ENTRY_REALM, tmp, TERM_L_BLUE);
119 * @ brief 年齢、身長、体重、社会的地位を表示する
120 * @param player_ptr プレイヤーへの参照ポインタ
122 * 日本語版では、身長はcmに、体重はkgに変更してある
124 static void display_phisique(PlayerType *player_ptr)
127 display_player_one_line(ENTRY_AGE, format("%d才", (int)player_ptr->age), TERM_L_BLUE);
128 display_player_one_line(ENTRY_HEIGHT, format("%dcm", (int)((player_ptr->ht * 254) / 100)), TERM_L_BLUE);
129 display_player_one_line(ENTRY_WEIGHT, format("%dkg", (int)((player_ptr->wt * 4536) / 10000)), TERM_L_BLUE);
130 display_player_one_line(ENTRY_SOCIAL, format("%d ", (int)player_ptr->sc), TERM_L_BLUE);
132 display_player_one_line(ENTRY_AGE, format("%d", (int)player_ptr->age), TERM_L_BLUE);
133 display_player_one_line(ENTRY_HEIGHT, format("%d", (int)player_ptr->ht), TERM_L_BLUE);
134 display_player_one_line(ENTRY_WEIGHT, format("%d", (int)player_ptr->wt), TERM_L_BLUE);
135 display_player_one_line(ENTRY_SOCIAL, format("%d", (int)player_ptr->sc), TERM_L_BLUE);
137 std::string alg = PlayerAlignment(player_ptr).get_alignment_description();
138 display_player_one_line(ENTRY_ALIGN, format("%s", alg.data()), TERM_L_BLUE);
142 * @brief 能力値を (減少していたら色を変えて)表示する
143 * @param player_ptr プレイヤーへの参照ポインタ
145 static void display_player_stats(PlayerType *player_ptr)
147 for (int i = 0; i < A_MAX; i++) {
148 if (player_ptr->stat_cur[i] < player_ptr->stat_max[i]) {
149 put_str(stat_names_reduced[i], 3 + i, 53);
150 int value = player_ptr->stat_use[i];
151 c_put_str(TERM_YELLOW, cnv_stat(value), 3 + i, 60);
152 value = player_ptr->stat_top[i];
153 c_put_str(TERM_L_GREEN, cnv_stat(value), 3 + i, 67);
155 put_str(stat_names[i], 3 + i, 53);
156 c_put_str(TERM_L_GREEN, cnv_stat(player_ptr->stat_use[i]), 3 + i, 60);
159 if (player_ptr->stat_max[i] == player_ptr->stat_max_max[i]) {
160 c_put_str(TERM_WHITE, "!", 3 + i, _(58, 58 - 2));
166 * @brief ゲームオーバーの原因を探る (生きていたら何もしない)
167 * @param player_ptr プレイヤーへの参照ポインタ
168 * @param statmsg メッセージバッファ
169 * @return 生きていたらFALSE、死んでいたらTRUE
171 static std::optional<std::string> search_death_cause(PlayerType *player_ptr)
173 auto *floor_ptr = player_ptr->current_floor_ptr;
174 if (!player_ptr->is_dead) {
178 if (w_ptr->total_winner) {
179 return std::string(format(_("…あなたは勝利の後%sした。", "...You %s after winning."),
180 streq(player_ptr->died_from, "Seppuku") ? _("切腹", "committed seppuku") : _("引退", "retired from the adventure")));
183 if (!floor_ptr->dun_level) {
185 return std::string(format("…あなたは%sで%sに殺された。", map_name(player_ptr), player_ptr->died_from.data()));
187 return std::string(format("...You were killed by %s in %s.", player_ptr->died_from.data(), map_name(player_ptr)));
191 if (inside_quest(floor_ptr->quest_number) && quest_type::is_fixed(floor_ptr->quest_number)) {
192 const auto &quest_list = QuestList::get_instance();
194 /* Get the quest text */
195 /* Bewere that INIT_ASSIGN resets the cur_num. */
196 init_flags = INIT_NAME_ONLY;
197 parse_fixed_map(player_ptr, QUEST_DEFINITION_LIST, 0, 0, 0, 0);
199 const auto *q_ptr = &quest_list[floor_ptr->quest_number];
201 return std::string(format("…あなたは、クエスト「%s」で%sに殺された。", q_ptr->name, player_ptr->died_from.data()));
203 return std::string(format("...You were killed by %s in the quest '%s'.", player_ptr->died_from.data(), q_ptr->name));
208 return std::string(format("…あなたは、%sの%d階で%sに殺された。", map_name(player_ptr), (int)floor_ptr->dun_level, player_ptr->died_from.data()));
210 return std::string(format("...You were killed by %s on level %d of %s.", player_ptr->died_from.data(), floor_ptr->dun_level, map_name(player_ptr)));
215 * @brief クエストフロアで生きている場合、クエスト名をバッファに詰める
216 * @param player_ptr プレイヤーへの参照ポインタ
217 * @param statmsg メッセージバッファ
218 * @return クエスト内であればTRUE、いなければFALSE
220 static std::optional<std::string> decide_death_in_quest(PlayerType *player_ptr)
222 auto *floor_ptr = player_ptr->current_floor_ptr;
223 if (!inside_quest(floor_ptr->quest_number) || !quest_type::is_fixed(floor_ptr->quest_number)) {
227 for (int i = 0; i < 10; i++) {
228 quest_text[i][0] = '\0';
231 const auto &quest_list = QuestList::get_instance();
233 init_flags = INIT_NAME_ONLY;
234 parse_fixed_map(player_ptr, QUEST_DEFINITION_LIST, 0, 0, 0, 0);
235 return std::string(format(_("…あなたは現在、 クエスト「%s」を遂行中だ。", "...Now, you are in the quest '%s'."), quest_list[floor_ptr->quest_number].name));
239 * @brief 現在いるフロアを、または死んでいたらどこでどう死んだかをバッファに詰める
240 * @param player_ptr プレイヤーへの参照ポインタ
241 * @param statmsg メッセージバッファ
243 static std::string decide_current_floor(PlayerType *player_ptr)
245 if (auto death_cause = search_death_cause(player_ptr);
246 death_cause.has_value() || !w_ptr->character_dungeon) {
247 return death_cause.value_or("");
250 auto *floor_ptr = player_ptr->current_floor_ptr;
251 if (floor_ptr->dun_level == 0) {
252 return std::string(format(_("…あなたは現在、 %s にいる。", "...Now, you are in %s."), map_name(player_ptr)));
255 if (auto decision = decide_death_in_quest(player_ptr); decision.has_value()) {
256 return decision.value();
260 return std::string(format("…あなたは現在、 %s の %d 階で探索している。", map_name(player_ptr), (int)floor_ptr->dun_level));
262 return std::string(format("...Now, you are exploring level %d of %s.", (int)floor_ptr->dun_level, map_name(player_ptr)));
267 * @brief プレイヤーのステータス表示メイン処理
268 * Display the character on the screen (various modes)
269 * @param player_ptr プレイヤーへの参照ポインタ
270 * @param tmp_mode 暫定表示モード (突然変異の有無で実際のモードに切り替える)
271 * @return 死亡原因となったモンスター名が複数行に亘る場合、表示に必要な行数. それ以外を表示する場合はnullopt
273 * 最初の1行と最後の2行は空行. / The top one and bottom two lines are left blank.
274 * Mode 0 = standard display with skills.
275 * Mode 1 = standard display with history.
276 * Mode 2 = summary of various things.
277 * Mode 3 = summary of various things (part 2).
278 * Mode 4 = mutations.
279 * Mode 5 = ??? (コード上の定義より6で割った余りは5になりうるが元のコメントに記載なし).
281 std::optional<int> display_player(PlayerType *player_ptr, const int tmp_mode)
283 auto has_any_mutation = (player_ptr->muta.any() || has_good_luck(player_ptr)) && display_mutations;
284 auto mode = has_any_mutation ? tmp_mode % 6 : tmp_mode % 5;
286 TermOffsetSetter tos(0, 0);
289 if (display_player_info(player_ptr, mode)) {
293 display_player_basic_info(player_ptr);
294 display_magic_realms(player_ptr);
295 if (PlayerClass(player_ptr).equals(PlayerClassType::CHAOS_WARRIOR) || (player_ptr->muta.has(PlayerMutationType::CHAOS_GIFT))) {
296 display_player_one_line(ENTRY_PATRON, patron_list[player_ptr->chaos_patron].name, TERM_L_BLUE);
299 display_phisique(player_ptr);
300 display_player_stats(player_ptr);
302 display_player_middle(player_ptr);
303 display_player_various(player_ptr);
307 put_str(_("(キャラクターの生い立ち)", "(Character Background)"), 11, 25);
308 for (auto i = 0; i < 4; i++) {
309 put_str(player_ptr->history[i], i + 12, 10);
312 auto statmsg = decide_current_floor(player_ptr);
313 if (statmsg.empty()) {
317 constexpr auto chars_per_line = 60;
318 return display_wrap_around(statmsg, chars_per_line, 17, 10);
322 * @brief プレイヤーの装備一覧をシンボルで並べる
324 * @param player_ptr プレイヤーへの参照ポインタ
325 * @param y 表示するコンソールの行
326 * @param x 表示するコンソールの列
328 * @todo y = 6、x = 0、mode = 0で固定。何とかする
330 void display_player_equippy(PlayerType *player_ptr, TERM_LEN y, TERM_LEN x, BIT_FLAGS16 mode)
332 const auto max_i = (mode & DP_WP) ? INVEN_BOW + 1 : INVEN_TOTAL;
333 for (int i = INVEN_MAIN_HAND; i < max_i; i++) {
334 const auto &o_ref = player_ptr->inventory_list[i];
335 auto a = o_ref.get_color();
336 auto c = o_ref.get_symbol();
337 if (!equippy_chars || (o_ref.bi_id == 0)) {
342 term_putch(x + i - INVEN_MAIN_HAND, y, a, c);