OSDN Git Service

Merge pull request #1774 from habu1010/feature/refactor-weapon-skill-table
[hengbandforosx/hengbandosx.git] / src / view / display-player.cpp
1 /*!
2  * @brief プレイヤーのステータス表示メインルーチン群
3  * @date 2020/02/25
4  * @author Hourier
5  * @details
6  * ここにこれ以上関数を引っ越してくるのは禁止
7  */
8
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 "object/object-kind.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 "status-first-page.h"
31 #include "system/floor-type-definition.h"
32 #include "system/object-type-definition.h"
33 #include "system/player-type-definition.h"
34 #include "term/screen-processor.h"
35 #include "term/term-color-types.h"
36 #include "util/buffer-shaper.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 "world/world.h"
43 #include <string>
44
45 /*!
46  * @brief
47  * @param player_ptr プレイヤーへの参照ポインタ
48  * @param mode ステータス表示モード
49  * @return どれかの処理をこなしたらTRUE、何もしなかったらFALSE
50  */
51 static bool display_player_info(player_type *player_ptr, int mode)
52 {
53     if (mode == 2) {
54         display_player_misc_info(player_ptr);
55         display_player_stat_info(player_ptr);
56         display_player_flag_info_1(player_ptr, display_player_equippy);
57         return true;
58     }
59
60     if (mode == 3) {
61         display_player_flag_info_2(player_ptr, display_player_equippy);
62         return true;
63     }
64
65     if (mode == 4) {
66         display_player_flag_info_3(player_ptr, display_player_equippy);
67         return true;
68     }
69
70     if (mode == 5) {
71         do_cmd_knowledge_mutations(player_ptr);
72         return true;
73     }
74
75     return false;
76 }
77
78 /*!
79  * @brief 名前、性別、種族、職業を表示する
80  * @param player_ptr プレイヤーへの参照ポインタ
81  */
82 static void display_player_basic_info(player_type *player_ptr)
83 {
84     char tmp[64];
85 #ifdef JP
86     sprintf(tmp, "%s%s%s", ap_ptr->title, ap_ptr->no == 1 ? "の" : "", player_ptr->name);
87 #else
88     sprintf(tmp, "%s %s", ap_ptr->title, player_ptr->name);
89 #endif
90
91     display_player_one_line(ENTRY_NAME, tmp, TERM_L_BLUE);
92     display_player_one_line(ENTRY_SEX, sp_ptr->title, TERM_L_BLUE);
93     display_player_one_line(ENTRY_RACE, (player_ptr->mimic_form ? mimic_info[player_ptr->mimic_form].title : rp_ptr->title), TERM_L_BLUE);
94     display_player_one_line(ENTRY_CLASS, cp_ptr->title, TERM_L_BLUE);
95 }
96
97 /*!
98  * @brief 魔法領域を表示する
99  * @param player_ptr プレイヤーへの参照ポインタ
100  */
101 static void display_magic_realms(player_type *player_ptr)
102 {
103     if (player_ptr->realm1 == REALM_NONE && player_ptr->element == REALM_NONE)
104         return;
105
106     char tmp[64];
107     if (player_ptr->pclass == PlayerClassType::ELEMENTALIST)
108         sprintf(tmp, "%s", get_element_title(player_ptr->element));
109     else if (player_ptr->realm2)
110         sprintf(tmp, "%s, %s", realm_names[player_ptr->realm1], realm_names[player_ptr->realm2]);
111     else
112         strcpy(tmp, realm_names[player_ptr->realm1]);
113
114     display_player_one_line(ENTRY_REALM, tmp, TERM_L_BLUE);
115 }
116
117 /*!
118  * @ brief 年齢、身長、体重、社会的地位を表示する
119  * @param player_ptr プレイヤーへの参照ポインタ
120  * @details
121  * 日本語版では、身長はcmに、体重はkgに変更してある
122  */
123 static void display_phisique(player_type *player_ptr)
124 {
125 #ifdef JP
126     display_player_one_line(ENTRY_AGE, format("%d才", (int)player_ptr->age), TERM_L_BLUE);
127     display_player_one_line(ENTRY_HEIGHT, format("%dcm", (int)((player_ptr->ht * 254) / 100)), TERM_L_BLUE);
128     display_player_one_line(ENTRY_WEIGHT, format("%dkg", (int)((player_ptr->wt * 4536) / 10000)), TERM_L_BLUE);
129     display_player_one_line(ENTRY_SOCIAL, format("%d  ", (int)player_ptr->sc), TERM_L_BLUE);
130 #else
131     display_player_one_line(ENTRY_AGE, format("%d", (int)player_ptr->age), TERM_L_BLUE);
132     display_player_one_line(ENTRY_HEIGHT, format("%d", (int)player_ptr->ht), TERM_L_BLUE);
133     display_player_one_line(ENTRY_WEIGHT, format("%d", (int)player_ptr->wt), TERM_L_BLUE);
134     display_player_one_line(ENTRY_SOCIAL, format("%d", (int)player_ptr->sc), TERM_L_BLUE);
135 #endif
136     std::string alg = PlayerAlignment(player_ptr).get_alignment_description();
137     display_player_one_line(ENTRY_ALIGN, format("%s", alg.c_str()), TERM_L_BLUE);
138 }
139
140 /*!
141  * @brief 能力値を (減少していたら色を変えて)表示する
142  * @param player_ptr プレイヤーへの参照ポインタ
143  */
144 static void display_player_stats(player_type *player_ptr)
145 {
146     char buf[80];
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             cnv_stat(value, buf);
152             c_put_str(TERM_YELLOW, buf, 3 + i, 60);
153             value = player_ptr->stat_top[i];
154             cnv_stat(value, buf);
155             c_put_str(TERM_L_GREEN, buf, 3 + i, 67);
156         } else {
157             put_str(stat_names[i], 3 + i, 53);
158             cnv_stat(player_ptr->stat_use[i], buf);
159             c_put_str(TERM_L_GREEN, buf, 3 + i, 60);
160         }
161
162         if (player_ptr->stat_max[i] == player_ptr->stat_max_max[i])
163             c_put_str(TERM_WHITE, "!", 3 + i, _(58, 58 - 2));
164     }
165 }
166
167 /*!
168  * @brief ゲームオーバーの原因を探る (生きていたら何もしない)
169  * @param player_ptr プレイヤーへの参照ポインタ
170  * @param statmsg メッセージバッファ
171  * @return 生きていたらFALSE、死んでいたらTRUE
172  */
173 static bool search_death_cause(player_type *player_ptr, char *statmsg)
174 {
175     floor_type *floor_ptr = player_ptr->current_floor_ptr;
176     if (!player_ptr->is_dead)
177         return false;
178
179     if (w_ptr->total_winner) {
180         sprintf(statmsg, _("…あなたは勝利の後%sした。", "...You %s after winning."),
181             streq(player_ptr->died_from, "Seppuku") ? _("切腹", "committed seppuku") : _("引退", "retired from the adventure"));
182
183         return true;
184     }
185
186     if (!floor_ptr->dun_level) {
187 #ifdef JP
188         sprintf(statmsg, "…あなたは%sで%sに殺された。", map_name(player_ptr), player_ptr->died_from);
189 #else
190         sprintf(statmsg, "...You were killed by %s in %s.", player_ptr->died_from, map_name(player_ptr));
191 #endif
192         return true;
193     }
194
195     if (floor_ptr->inside_quest && quest_type::is_fixed(floor_ptr->inside_quest)) {
196         /* Get the quest text */
197         /* Bewere that INIT_ASSIGN resets the cur_num. */
198         init_flags = INIT_NAME_ONLY;
199         parse_fixed_map(player_ptr, "q_info.txt", 0, 0, 0, 0);
200 #ifdef JP
201         sprintf(statmsg, "…あなたは、クエスト「%s」で%sに殺された。", quest[floor_ptr->inside_quest].name, player_ptr->died_from);
202 #else
203         sprintf(statmsg, "...You were killed by %s in the quest '%s'.", player_ptr->died_from, quest[floor_ptr->inside_quest].name);
204 #endif
205         return true;
206     }
207
208 #ifdef JP
209     sprintf(statmsg, "…あなたは、%sの%d階で%sに殺された。", map_name(player_ptr), (int)floor_ptr->dun_level, player_ptr->died_from);
210 #else
211     sprintf(statmsg, "...You were killed by %s on level %d of %s.", player_ptr->died_from, floor_ptr->dun_level, map_name(player_ptr));
212 #endif
213
214     return true;
215 }
216
217 /*!
218  * @brief クエストフロアで生きている場合、クエスト名をバッファに詰める
219  * @param player_ptr プレイヤーへの参照ポインタ
220  * @param statmsg メッセージバッファ
221  * @return クエスト内であればTRUE、いなければFALSE
222  */
223 static bool decide_death_in_quest(player_type *player_ptr, char *statmsg)
224 {
225     floor_type *floor_ptr = player_ptr->current_floor_ptr;
226     if (!floor_ptr->inside_quest || !quest_type::is_fixed(floor_ptr->inside_quest))
227         return false;
228
229     for (int i = 0; i < 10; i++)
230         quest_text[i][0] = '\0';
231
232     quest_text_line = 0;
233     init_flags = INIT_NAME_ONLY;
234     parse_fixed_map(player_ptr, "q_info.txt", 0, 0, 0, 0);
235     sprintf(statmsg, _("…あなたは現在、 クエスト「%s」を遂行中だ。", "...Now, you are in the quest '%s'."), quest[floor_ptr->inside_quest].name);
236     return true;
237 }
238
239 /*!
240  * @brief 現在いるフロアを、または死んでいたらどこでどう死んだかをバッファに詰める
241  * @param player_ptr プレイヤーへの参照ポインタ
242  * @param statmsg メッセージバッファ
243  */
244 static void decide_current_floor(player_type *player_ptr, char *statmsg)
245 {
246     if (search_death_cause(player_ptr, statmsg))
247         return;
248     if (!w_ptr->character_dungeon)
249         return;
250
251     floor_type *floor_ptr = player_ptr->current_floor_ptr;
252     if (floor_ptr->dun_level == 0) {
253         sprintf(statmsg, _("…あなたは現在、 %s にいる。", "...Now, you are in %s."), map_name(player_ptr));
254         return;
255     }
256
257     if (decide_death_in_quest(player_ptr, statmsg))
258         return;
259
260 #ifdef JP
261     sprintf(statmsg, "…あなたは現在、 %s の %d 階で探索している。", map_name(player_ptr), (int)floor_ptr->dun_level);
262 #else
263     sprintf(statmsg, "...Now, you are exploring level %d of %s.", (int)floor_ptr->dun_level, map_name(player_ptr));
264 #endif
265 }
266
267 /*!
268  * @brief 今いる、または死亡した場所を表示する
269  * @param statmsg メッセージバッファ
270  */
271 static void display_current_floor(char *statmsg)
272 {
273     char temp[128];
274     shape_buffer(statmsg, 60, temp, sizeof(temp));
275     char *t;
276     t = temp;
277     for (int i = 0; i < 2; i++) {
278         if (t[0] == 0)
279             return;
280
281         put_str(t, i + 5 + 12, 10);
282         t += strlen(t) + 1;
283     }
284 }
285
286 /*!
287  * @brief プレイヤーのステータス表示メイン処理
288  * Display the character on the screen (various modes)
289  * @param player_ptr プレイヤーへの参照ポインタ
290  * @param mode 表示モードID
291  * @details
292  * <pre>
293  * The top one and bottom two lines are left blank.
294  * Mode 0 = standard display with skills
295  * Mode 1 = standard display with history
296  * Mode 2 = summary of various things
297  * Mode 3 = summary of various things (part 2)
298  * Mode 4 = mutations
299  * </pre>
300  */
301 void display_player(player_type *player_ptr, int mode)
302 {
303     if ((player_ptr->muta.any() || has_good_luck(player_ptr)) && display_mutations)
304         mode = (mode % 6);
305     else
306         mode = (mode % 5);
307
308     clear_from(0);
309     if (display_player_info(player_ptr, mode))
310         return;
311
312     display_player_basic_info(player_ptr);
313     display_magic_realms(player_ptr);
314
315     if ((player_ptr->pclass == PlayerClassType::CHAOS_WARRIOR) || (player_ptr->muta.has(MUTA::CHAOS_GIFT)))
316         display_player_one_line(ENTRY_PATRON, patron_list[player_ptr->chaos_patron].name.c_str(), TERM_L_BLUE);
317
318     display_phisique(player_ptr);
319     display_player_stats(player_ptr);
320
321     if (mode == 0) {
322         display_player_middle(player_ptr);
323         display_player_various(player_ptr);
324         return;
325     }
326
327     char statmsg[1000];
328     put_str(_("(キャラクターの生い立ち)", "(Character Background)"), 11, 25);
329     for (int i = 0; i < 4; i++)
330         put_str(player_ptr->history[i], i + 12, 10);
331
332     *statmsg = '\0';
333     decide_current_floor(player_ptr, statmsg);
334     if (!*statmsg)
335         return;
336
337     display_current_floor(statmsg);
338 }
339
340 /*!
341  * @brief プレイヤーの装備一覧をシンボルで並べる
342  * Equippy chars
343  * @param player_ptr プレイヤーへの参照ポインタ
344  * @param y 表示するコンソールの行
345  * @param x 表示するコンソールの列
346  * @param mode オプション
347  * @todo y = 6、x = 0、mode = 0で固定。何とかする
348  */
349 void display_player_equippy(player_type *player_ptr, TERM_LEN y, TERM_LEN x, BIT_FLAGS16 mode)
350 {
351     int max_i = (mode & DP_WP) ? INVEN_BOW + 1 : INVEN_TOTAL;
352     for (int i = INVEN_MAIN_HAND; i < max_i; i++) {
353         object_type *o_ptr;
354         o_ptr = &player_ptr->inventory_list[i];
355
356         TERM_COLOR a = object_attr(o_ptr);
357         SYMBOL_CODE c = object_char(o_ptr);
358
359         if (!equippy_chars || !o_ptr->k_idx) {
360             c = ' ';
361             a = TERM_DARK;
362         }
363
364         term_putch(x + i - INVEN_MAIN_HAND, y, a, c);
365     }
366 }