1 #include "window/main-window-left-frame.h"
2 #include "game-option/special-options.h"
3 #include "game-option/text-display-options.h"
4 #include "market/arena-info-table.h"
5 #include "monster-race/monster-race.h"
6 #include "monster/monster-status.h"
7 #include "player-base/player-race.h"
8 #include "player-info/class-info.h"
9 #include "player-info/mimic-info-table.h"
10 #include "player/player-status-table.h"
11 #include "system/floor-type-definition.h"
12 #include "system/monster-entity.h"
13 #include "system/monster-race-info.h"
14 #include "system/player-type-definition.h"
15 #include "term/gameterm.h"
16 #include "term/screen-processor.h"
17 #include "term/term-color-types.h"
18 #include "term/z-form.h"
19 #include "timed-effect/player-hallucination.h"
20 #include "timed-effect/timed-effects.h"
21 #include "util/string-processor.h"
22 #include "window/main-window-row-column.h"
23 #include "window/main-window-stat-poster.h"
24 #include "window/main-window-util.h"
25 #include "world/world.h"
28 * @brief ターゲットしているモンスターの情報部に表示する状態異常と文字色の対応を保持する構造体
30 struct condition_layout_info {
36 * @brief プレイヤーの称号を表示する / Prints "title", including "wizard" or "winner" as needed.
38 void print_title(PlayerType *player_ptr)
43 p = _("[ウィザード]", "[=-WIZARD-=]");
44 } else if (w_ptr->total_winner) {
45 if (player_ptr->is_true_winner()) {
46 p = _("*真・勝利者*", "*TRUEWINNER*");
48 p = _("***勝利者***", "***WINNER***");
51 angband_strcpy(str, player_titles[enum2i(player_ptr->pclass)][(player_ptr->lev - 1) / 5], sizeof(str));
55 print_field(p, ROW_TITLE, COL_TITLE);
59 * @brief プレイヤーのレベルを表示する / Prints level
61 void print_level(PlayerType *player_ptr)
63 std::string tmp = format("%5d", player_ptr->lev);
64 if (player_ptr->lev >= player_ptr->max_plv) {
65 put_str(_("レベル ", "LEVEL "), ROW_LEVEL, 0);
66 c_put_str(TERM_L_GREEN, tmp, ROW_LEVEL, COL_LEVEL + 7);
68 put_str(_("xレベル", "Level "), ROW_LEVEL, 0);
69 c_put_str(TERM_YELLOW, tmp, ROW_LEVEL, COL_LEVEL + 7);
74 * @brief プレイヤーの経験値を表示する / Display the experience
76 void print_exp(PlayerType *player_ptr)
80 PlayerRace pr(player_ptr);
81 if ((!exp_need) || pr.equals(PlayerRaceType::ANDROID)) {
82 out_val = format("%8ld", (long)player_ptr->exp);
84 if (player_ptr->lev >= PY_MAX_LEVEL) {
87 out_val = format("%8ld", (long)(player_exp[player_ptr->lev - 1] * player_ptr->expfact / 100L) - player_ptr->exp);
91 if (player_ptr->exp >= player_ptr->max_exp) {
92 if (pr.equals(PlayerRaceType::ANDROID)) {
93 put_str(_("強化 ", "Cst "), ROW_EXP, 0);
95 put_str(_("経験 ", "EXP "), ROW_EXP, 0);
97 c_put_str(TERM_L_GREEN, out_val, ROW_EXP, COL_EXP + 4);
99 put_str(_("x経験", "Exp "), ROW_EXP, 0);
100 c_put_str(TERM_YELLOW, out_val, ROW_EXP, COL_EXP + 4);
105 * @brief プレイヤーのACを表示する / Prints current AC
107 void print_ac(PlayerType *player_ptr)
109 /* AC の表示方式を変更している */
110 put_str(_(" AC( )", "Cur AC "), ROW_AC, COL_AC);
111 c_put_str(TERM_L_GREEN, format("%5d", player_ptr->dis_ac + player_ptr->dis_to_a), ROW_AC, COL_AC + _(6, 7));
115 * @brief プレイヤーのHPを表示する / Prints Cur/Max hit points
117 void print_hp(PlayerType *player_ptr)
119 put_str("HP", ROW_CURHP, COL_CURHP);
121 if (player_ptr->chp >= player_ptr->mhp) {
122 color = TERM_L_GREEN;
123 } else if (player_ptr->chp > (player_ptr->mhp * hitpoint_warn) / 10) {
129 c_put_str(color, format("%4ld", (long int)player_ptr->chp), ROW_CURHP, COL_CURHP + 3);
130 put_str("/", ROW_CURHP, COL_CURHP + 7);
131 color = TERM_L_GREEN;
132 c_put_str(color, format("%4ld", (long int)player_ptr->mhp), ROW_CURHP, COL_CURHP + 8);
136 * @brief プレイヤーのMPを表示する / Prints players max/cur spell points
138 void print_sp(PlayerType *player_ptr)
140 if ((mp_ptr->spell_book == ItemKindType::NONE) && mp_ptr->spell_first == SPELL_FIRST_NO_SPELL) {
144 put_str(_("MP", "SP"), ROW_CURSP, COL_CURSP);
146 if (player_ptr->csp >= player_ptr->msp) {
147 color = TERM_L_GREEN;
148 } else if (player_ptr->csp > (player_ptr->msp * mana_warn) / 10) {
154 c_put_str(color, format("%4ld", (long int)player_ptr->csp), ROW_CURSP, COL_CURSP + 3);
155 put_str("/", ROW_CURSP, COL_CURSP + 7);
156 color = TERM_L_GREEN;
157 c_put_str(color, format("%4ld", (long int)player_ptr->msp), ROW_CURSP, COL_CURSP + 8);
161 * @brief プレイヤーの所持金を表示する / Prints current gold
162 * @param player_ptr プレイヤーへの参照ポインタ
164 void print_gold(PlayerType *player_ptr)
166 put_str(_("$ ", "AU "), ROW_GOLD, COL_GOLD);
167 c_put_str(TERM_L_GREEN, format("%9ld", (long)player_ptr->au), ROW_GOLD, COL_GOLD + 3);
171 * @brief 現在のフロアの深さを表示する / Prints depth in stat area
172 * @param player_ptr プレイヤーへの参照ポインタ
174 void print_depth(PlayerType *player_ptr)
176 TERM_COLOR attr = TERM_WHITE;
179 term_get_size(&wid, &hgt);
180 TERM_LEN col_depth = wid + COL_DEPTH;
181 TERM_LEN row_depth = hgt + ROW_DEPTH;
183 auto *floor_ptr = player_ptr->current_floor_ptr;
184 if (!floor_ptr->dun_level) {
185 c_prt(attr, format("%7s", _("地上", "Surf.")), row_depth, col_depth);
189 if (inside_quest(floor_ptr->quest_number) && !floor_ptr->dungeon_idx) {
190 c_prt(attr, format("%7s", _("地上", "Quest")), row_depth, col_depth);
196 depths = format(_("%d ft", "%d ft"), (int)floor_ptr->dun_level * 50);
198 depths = format(_("%d 階", "Lev %d"), (int)floor_ptr->dun_level);
201 switch (player_ptr->feeling) {
210 break; /* Horrible visions */
213 break; /* Very dangerous */
216 break; /* Very bad feeling */
219 break; /* Bad feeling */
225 break; /* Luck is turning */
228 break; /* Don't like */
231 break; /* Reasonably safe */
234 break; /* Boring place */
237 c_prt(attr, format("%7s", depths.data()), row_depth, col_depth);
241 * @brief プレイヤーのステータスを一括表示する(左側部分) / Display basic info (mostly left of map)
242 * @param player_ptr プレイヤーへの参照ポインタ
244 void print_frame_basic(PlayerType *player_ptr)
246 if (player_ptr->mimic_form != MimicKindType::NONE) {
247 print_field(mimic_info.at(player_ptr->mimic_form).title, ROW_RACE, COL_RACE);
250 angband_strcpy(str, rp_ptr->title, sizeof(str));
251 print_field(str, ROW_RACE, COL_RACE);
254 print_title(player_ptr);
255 print_level(player_ptr);
256 print_exp(player_ptr);
257 for (int i = 0; i < A_MAX; i++) {
258 print_stat(player_ptr, i);
261 print_ac(player_ptr);
262 print_hp(player_ptr);
263 print_sp(player_ptr);
264 print_gold(player_ptr);
265 print_depth(player_ptr);
266 print_health(player_ptr, true);
267 print_health(player_ptr, false);
271 * @brief wizardモード中の闘技場情報を表示する
272 * @param player_ptr プレイヤーへの参照ポインタ
274 static void print_health_monster_in_arena_for_wizard(PlayerType *player_ptr)
276 int row = ROW_INFO - 1;
277 int col = COL_INFO + 2;
279 const int max_num_of_monster_in_arena = 4;
281 for (int i = 0; i < max_num_of_monster_in_arena; i++) {
283 auto monster_list_index = i + 1; // m_listの1-4に闘技場のモンスターデータが入っている
285 term_putstr(col - 2, row + row_offset, 12, TERM_WHITE, " / ");
287 auto &monster = player_ptr->current_floor_ptr->m_list[monster_list_index];
288 if (MonsterRace(monster.r_idx).is_valid()) {
289 term_putstr(col - 2, row + row_offset, 2, monraces_info[monster.r_idx].x_attr,
290 format("%c", monraces_info[monster.r_idx].x_char));
291 term_putstr(col - 1, row + row_offset, 5, TERM_WHITE, format("%5d", monster.hp));
292 term_putstr(col + 5, row + row_offset, 6, TERM_WHITE, format("%5d", monster.max_maxhp));
298 * @brief 対象のモンスターからcondition_layout_infoのリストを生成して返す
299 * @param monster 対象のモンスター
300 * @return condition_layout_infoのリスト
302 static std::vector<condition_layout_info> get_condition_layout_info(const MonsterEntity &monster)
304 std::vector<condition_layout_info> result;
306 if (monster.is_invulnerable()) {
307 result.push_back({ effect_type_to_label.at(MTIMED_INVULNER), TERM_WHITE });
309 if (monster.is_accelerated()) {
310 result.push_back({ effect_type_to_label.at(MTIMED_FAST), TERM_L_GREEN });
312 if (monster.is_decelerated()) {
313 result.push_back({ effect_type_to_label.at(MTIMED_SLOW), TERM_UMBER });
315 if (monster.is_fearful()) {
316 result.push_back({ effect_type_to_label.at(MTIMED_MONFEAR), TERM_SLATE });
318 if (monster.is_confused()) {
319 result.push_back({ effect_type_to_label.at(MTIMED_CONFUSED), TERM_L_UMBER });
321 if (monster.is_asleep()) {
322 result.push_back({ effect_type_to_label.at(MTIMED_CSLEEP), TERM_BLUE });
324 if (monster.is_stunned()) {
325 result.push_back({ effect_type_to_label.at(MTIMED_STUNNED), TERM_ORANGE });
332 * @brief モンスターの体力ゲージを表示する
333 * @param riding TRUEならば騎乗中のモンスターの体力、FALSEならターゲットモンスターの体力を表示する。表示位置は固定。
336 * Redraw the "monster health bar" -DRS-
337 * Rather extensive modifications by -BEN-
339 * The "monster health bar" provides visual feedback on the "health"
340 * of the monster currently being "tracked". There are several ways
341 * to "track" a monster, including targetting it, attacking it, and
342 * affecting it (and nobody else) with a ranged attack.
344 * Display the monster health bar (affectionately known as the
345 * "health-o-meter"). Clear health bar if nothing is being tracked.
346 * Auto-track current target monster when bored. Note that the
347 * health-bar stops tracking any monster that "disappears".
350 void print_health(PlayerType *player_ptr, bool riding)
352 std::optional<short> monster_idx;
356 if (player_ptr->riding > 0) {
357 monster_idx = player_ptr->riding;
359 row = ROW_RIDING_INFO;
360 col = COL_RIDING_INFO;
362 // ウィザードモードで闘技場観戦時の表示
363 if (w_ptr->wizard && player_ptr->phase_out) {
364 print_health_monster_in_arena_for_wizard(player_ptr);
367 if (player_ptr->health_who > 0) {
368 monster_idx = player_ptr->health_who;
374 const int max_width = 12; // 表示幅
376 TERM_LEN width, height;
377 term_get_size(&width, &height);
378 const auto extra_line_count = riding ? 0 : height - MAIN_TERM_MIN_ROWS;
379 for (auto y = row; y < row + extra_line_count + 1; ++y) {
380 term_erase(col, y, max_width);
383 if (!monster_idx.has_value()) {
387 const auto &monster = player_ptr->current_floor_ptr->m_list[monster_idx.value()];
389 if ((!monster.ml) || (player_ptr->effects()->hallucination()->is_hallucinated()) || monster.is_dead()) {
390 term_putstr(col, row, max_width, TERM_WHITE, "[----------]");
394 const auto [hit_point_bar_color, len] = monster.get_hp_bar_data();
396 term_putstr(col, row, max_width, TERM_WHITE, "[----------]");
397 term_putstr(col + 1, row, len, hit_point_bar_color, "**********");
399 // 騎乗中のモンスターの状態異常は表示しない
408 // MAX_WIDTHを超えたら次の行に移動する
409 for (const auto &info : get_condition_layout_info(monster)) {
410 if (row_offset > extra_line_count) {
413 if (col_offset + info.label.length() > max_width) { // 改行が必要かどうかチェック
417 term_putstr(col + col_offset, row + row_offset, max_width, info.color, info.label);
418 col_offset += info.label.length() + 1; // 文字数と空白の分だけoffsetを加算