OSDN Git Service

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