OSDN Git Service

Merge pull request #947 from shimitei/feature/#870_describe_music_and_sound
[hengbandforosx/hengbandosx.git] / src / player / process-death.cpp
1 /*!
2  * @brief 死亡・引退・切腹時の画面表示
3  * @date 2020/02/24
4  * @author Hourier
5  * @details
6  * core、files、view-mainwindowの参照禁止。コールバックで対応すること
7  */
8
9 #include "process-death.h"
10 #include "core/asking-player.h"
11 #include "core/player-update-types.h"
12 #include "core/stuff-handler.h"
13 #include "flavor/flavor-describer.h"
14 #include "floor/floor-town.h"
15 #include "game-option/game-play-options.h"
16 #include "inventory/inventory-slot-types.h"
17 #include "io/input-key-acceptor.h"
18 #include "object/item-use-flags.h"
19 #include "perception/object-perception.h"
20 #include "player/player-class.h"
21 #include "store/store-util.h"
22 #include "store/store.h"
23 #include "system/floor-type-definition.h"
24 #include "system/object-type-definition.h"
25 #include "system/player-type-definition.h"
26 #include "term/gameterm.h"
27 #include "term/screen-processor.h"
28 #include "util/buffer-shaper.h"
29 #include "util/int-char-converter.h"
30 #include "util/string-processor.h"
31 #include "view/display-inventory.h"
32 #include "view/display-messages.h"
33 #include "world/world.h"
34
35 #define GRAVE_LINE_WIDTH 31
36
37 /*!
38  * @brief 墓石の真ん中に文字列を書き込む /
39  * Centers a string within a GRAVE_LINE_WIDTH character string          -JWT-
40  * @return なし
41  * @details
42  */
43 static void center_string(char *buf, concptr str)
44 {
45     int i = strlen(str);
46     int j = GRAVE_LINE_WIDTH / 2 - i / 2;
47     (void)sprintf(buf, "%*s%s%*s", j, "", str, GRAVE_LINE_WIDTH - i - j, "");
48 }
49
50 /*!
51  * @brief 墓に基本情報を表示
52  * @param dead_ptr プレーヤーへの参照ポインタ
53  * @param buf 墓テンプレ
54  * @return なし
55  */
56 static void show_basic_params(player_type *dead_ptr, char *buf)
57 {
58     char tomb_message[160];
59     (void)sprintf(tomb_message, _("レベル: %d", "Level: %d"), (int)dead_ptr->lev);
60     center_string(buf, tomb_message);
61     put_str(buf, 11, 11);
62
63     (void)sprintf(tomb_message, _("経験値: %ld", "Exp: %ld"), (long)dead_ptr->exp);
64     center_string(buf, tomb_message);
65     put_str(buf, 12, 11);
66
67     (void)sprintf(tomb_message, _("所持金: %ld", "AU: %ld"), (long)dead_ptr->au);
68     center_string(buf, tomb_message);
69     put_str(buf, 13, 11);
70 }
71
72 #ifdef JP
73 /*!
74  * @brief プレーヤーを殺したモンスターを表示する (日本語版専用)
75  * @param dead_ptr プレーヤーへの参照ポインタ
76  * @param buf 墓テンプレ
77  * @param tomb_message 墓碑に刻む言葉
78  * @return 追加の行数
79  */
80 static int show_killing_monster(player_type *dead_ptr, char *buf, char *tomb_message, size_t tomb_message_size)
81 {
82     shape_buffer(dead_ptr->died_from, GRAVE_LINE_WIDTH + 1, tomb_message, tomb_message_size);
83     char *t;
84     t = tomb_message + strlen(tomb_message) + 1;
85     if (!*t)
86         return 0;
87
88     char killer[MAX_MONSTER_NAME];
89     strcpy(killer, t); /* 2nd line */
90     if (*(t + strlen(t) + 1)) /* Does 3rd line exist? */
91     {
92         for (t = killer + strlen(killer) - 2; iskanji(*(t - 1)); t--) /* Loop */
93             ;
94         strcpy(t, "…");
95     } else if (angband_strstr(tomb_message, "『") && suffix(killer, "』")) {
96         char killer2[MAX_MONSTER_NAME];
97         char *name_head = angband_strstr(tomb_message, "『");
98         sprintf(killer2, "%s%s", name_head, killer);
99         if (strlen(killer2) <= GRAVE_LINE_WIDTH) {
100             strcpy(killer, killer2);
101             *name_head = '\0';
102         }
103     } else if (angband_strstr(tomb_message, "「") && suffix(killer, "」")) {
104         char killer2[MAX_MONSTER_NAME];
105         char *name_head = angband_strstr(tomb_message, "「");
106         sprintf(killer2, "%s%s", name_head, killer);
107         if (strlen(killer2) <= GRAVE_LINE_WIDTH) {
108             strcpy(killer, killer2);
109             *name_head = '\0';
110         }
111     }
112
113     center_string(buf, killer);
114     put_str(buf, 15, 11);
115     return 1;
116 }
117
118 /*!
119  * @brief どこで死んだかを表示する (日本語版専用)
120  * @param dead_ptr プレーヤーへの参照ポインタ
121  * @param buf 墓テンプレ
122  * @param tomb_message 表示する文字列
123  * @param extra_line 追加の行数
124  * @return なし
125  */
126 static void show_dead_place(player_type *dead_ptr, char *buf, char *tomb_message, int extra_line)
127 {
128     if (streq(dead_ptr->died_from, "ripe") || streq(dead_ptr->died_from, "Seppuku"))
129         return;
130
131     if (dead_ptr->current_floor_ptr->dun_level == 0) {
132         concptr field_name = dead_ptr->town_num ? "街" : "荒野";
133         if (streq(dead_ptr->died_from, "途中終了")) {
134             sprintf(tomb_message, "%sで死んだ", field_name);
135         } else {
136             sprintf(tomb_message, "に%sで殺された", field_name);
137         }
138     } else {
139         if (streq(dead_ptr->died_from, "途中終了")) {
140             sprintf(tomb_message, "地下 %d 階で死んだ", (int)dead_ptr->current_floor_ptr->dun_level);
141         } else {
142             sprintf(tomb_message, "に地下 %d 階で殺された", (int)dead_ptr->current_floor_ptr->dun_level);
143         }
144     }
145
146     center_string(buf, tomb_message);
147     put_str(buf, 15 + extra_line, 11);
148 }
149
150 /*!
151  * @brief 墓に刻む言葉を細かく表示 (日本語版専用)
152  * @param dead_ptr プレーヤーへの参照ポインタ
153  * @param buf 墓テンプレ
154  * @return なし
155  */
156 static void show_tomb_detail(player_type *dead_ptr, char *buf)
157 {
158     char tomb_message[160];
159     int extra_line = 0;
160     if (streq(dead_ptr->died_from, "途中終了")) {
161         strcpy(tomb_message, "<自殺>");
162     } else if (streq(dead_ptr->died_from, "ripe")) {
163         strcpy(tomb_message, "引退後に天寿を全う");
164     } else if (streq(dead_ptr->died_from, "Seppuku")) {
165         strcpy(tomb_message, "勝利の後、切腹");
166     } else {
167         extra_line = show_killing_monster(dead_ptr, buf, tomb_message, sizeof(tomb_message));
168     }
169
170     center_string(buf, tomb_message);
171     put_str(buf, 14, 11);
172
173     show_dead_place(dead_ptr, buf, tomb_message, extra_line);
174 }
175 #else
176
177 /*!
178  * @brief Detailed display of words engraved on the tomb (English version only)
179  * @param dead_ptr reference pointer to the player
180  * @param buf template of the tomb
181  * @return nothing
182  */
183 static void show_tomb_detail(player_type *dead_ptr, char *buf)
184 {
185     char tomb_message[160];
186     (void)sprintf(tomb_message, "Killed on Level %d", dead_ptr->current_floor_ptr->dun_level);
187     center_string(buf, tomb_message);
188     put_str(buf, 14, 11);
189
190     shape_buffer(format("by %s.", dead_ptr->died_from), GRAVE_LINE_WIDTH + 1, tomb_message, sizeof(tomb_message));
191     center_string(buf, tomb_message);
192     char *t;
193     put_str(buf, 15, 11);
194     t = tomb_message + strlen(tomb_message) + 1;
195     if (!*t)
196         return;
197
198     char killer[MAX_MONSTER_NAME];
199     strcpy(killer, t); /* 2nd line */
200     if (*(t + strlen(t) + 1)) /* Does 3rd line exist? */
201     {
202         int dummy_len = strlen(killer);
203         strcpy(killer + MIN(dummy_len, GRAVE_LINE_WIDTH - 3), "...");
204     }
205
206     center_string(buf, killer);
207     put_str(buf, 16, 11);
208 }
209 #endif
210
211 /*!
212  * @brief 墓石のアスキーアート表示 /
213  * Display a "tomb-stone"
214  * @param creature_ptr プレーヤーへの参照ポインタ
215  * @return なし
216  */
217 void print_tomb(player_type *dead_ptr)
218 {
219     term_clear();
220     char buf[1024];
221     read_dead_file(buf, sizeof(buf));
222     concptr p = (current_world_ptr->total_winner || (dead_ptr->lev > PY_MAX_LEVEL)) ? _("偉大なる者", "Magnificent")
223                                                                                     : player_title[dead_ptr->pclass][(dead_ptr->lev - 1) / 5];
224
225     center_string(buf, dead_ptr->name);
226     put_str(buf, 6, 11);
227
228 #ifdef JP
229 #else
230     center_string(buf, "the");
231     put_str(buf, 7, 11);
232 #endif
233
234     center_string(buf, p);
235     put_str(buf, 8, 11);
236
237     center_string(buf, cp_ptr->title);
238     put_str(buf, 10, 11);
239
240     show_basic_params(dead_ptr, buf);
241     show_tomb_detail(dead_ptr, buf);
242
243     char current_time[80];
244     time_t ct = time((time_t *)0);
245     (void)sprintf(current_time, "%-.24s", ctime(&ct));
246     center_string(buf, current_time);
247     put_str(buf, 17, 11);
248     msg_format(_("さようなら、%s!", "Goodbye, %s!"), dead_ptr->name);
249 }
250
251 /*!
252  * @brief 死亡/引退/切腹時にインベントリ内のアイテムを*鑑定*する
253  * @param creature_ptr プレーヤーへの参照ポインタ
254  * @return なし
255  */
256 static void inventory_aware(player_type *creature_ptr)
257 {
258     object_type *o_ptr;
259     for (int i = 0; i < INVEN_TOTAL; i++) {
260         o_ptr = &creature_ptr->inventory_list[i];
261         if (!o_ptr->k_idx)
262             continue;
263
264         object_aware(creature_ptr, o_ptr);
265         object_known(o_ptr);
266     }
267 }
268
269 /*!
270  * @brief 死亡/引退/切腹時に我が家のアイテムを*鑑定*する
271  * @param creature_ptr プレーヤーへの参照ポインタ
272  * @return なし
273  */
274 static void home_aware(player_type *creature_ptr)
275 {
276     object_type *o_ptr;
277     store_type *store_ptr;
278     for (int i = 1; i < max_towns; i++) {
279         store_ptr = &town_info[i].store[STORE_HOME];
280         for (int j = 0; j < store_ptr->stock_num; j++) {
281             o_ptr = &store_ptr->stock[j];
282             if (!o_ptr->k_idx)
283                 continue;
284
285             object_aware(creature_ptr, o_ptr);
286             object_known(o_ptr);
287         }
288     }
289 }
290
291 /*!
292  * @brief プレーヤーの持ち物を表示する
293  * @param creature_ptr プレーヤーへの参照ポインタ
294  * @return Escキーでゲームを終了する時TRUE
295  */
296 static bool show_dead_player_items(player_type *creature_ptr)
297 {
298     if (creature_ptr->equip_cnt) {
299         term_clear();
300         (void)show_equipment(creature_ptr, 0, USE_FULL, TV_NONE);
301         prt(_("装備していたアイテム: -続く-", "You are using: -more-"), 0, 0);
302         if (inkey() == ESCAPE)
303             return TRUE;
304     }
305
306     if (creature_ptr->inven_cnt) {
307         term_clear();
308         (void)show_inventory(creature_ptr, 0, USE_FULL, TV_NONE);
309         prt(_("持っていたアイテム: -続く-", "You are carrying: -more-"), 0, 0);
310
311         if (inkey() == ESCAPE)
312             return TRUE;
313     }
314
315     return FALSE;
316 }
317
318 /*!
319  * @brief 我が家にあったアイテムを表示する
320  * @param creature_ptr プレーヤーへの参照ポインタ
321  * @return なし
322  */
323 static void show_dead_home_items(player_type *creature_ptr)
324 {
325     for (int l = 1; l < max_towns; l++) {
326         store_type *store_ptr;
327         store_ptr = &town_info[l].store[STORE_HOME];
328         if (store_ptr->stock_num == 0)
329             continue;
330
331         for (int i = 0, k = 0; i < store_ptr->stock_num; k++) {
332             term_clear();
333             for (int j = 0; (j < 12) && (i < store_ptr->stock_num); j++, i++) {
334                 GAME_TEXT o_name[MAX_NLEN];
335                 char tmp_val[80];
336                 object_type *o_ptr;
337                 o_ptr = &store_ptr->stock[i];
338                 sprintf(tmp_val, "%c) ", I2A(j));
339                 prt(tmp_val, j + 2, 4);
340                 describe_flavor(creature_ptr, o_name, o_ptr, 0);
341                 c_put_str(tval_to_attr[o_ptr->tval], o_name, j + 2, 7);
342             }
343
344             prt(format(_("我が家に置いてあったアイテム ( %d ページ): -続く-", "Your home contains (page %d): -more-"), k + 1), 0, 0);
345             if (inkey() == ESCAPE)
346                 return;
347         }
348     }
349 }
350
351 /*!
352  * @brief キャラクタ情報をファイルに書き出す
353  * @param creature_ptr プレーヤーへの参照ポインタ
354  * @param file_character ステータスダンプへのコールバック
355  * @return なし
356  */
357 static void export_player_info(player_type *creature_ptr, display_player_pf display_player)
358 {
359     prt(_("キャラクターの記録をファイルに書き出すことができます。", "You may now dump a character record to one or more files."), 21, 0);
360     prt(_("リターンキーでキャラクターを見ます。ESCで中断します。", "Then, hit RETURN to see the character, or ESC to abort."), 22, 0);
361     while (TRUE) {
362         char out_val[160];
363         put_str(_("ファイルネーム: ", "Filename: "), 23, 0);
364         strcpy(out_val, "");
365         if (!askfor(out_val, 60))
366             return;
367         if (!out_val[0])
368             break;
369
370         screen_save();
371         (void)file_character(creature_ptr, out_val, display_player);
372         screen_load();
373     }
374 }
375
376 /*!
377  * @brief 自動的にプレイヤーステータスをファイルダンプ出力する
378  * @return なし
379  */
380 static void file_character_auto(player_type *creature_ptr, display_player_pf display_player)
381 {
382     time_t now_t = time(NULL);
383     struct tm *now_tm = localtime(&now_t);
384
385     char datetime[32];
386     char filename[128];
387
388     strftime(datetime, sizeof(datetime), "%Y-%m-%d_%H%M%S", now_tm);
389     strnfmt(filename, sizeof(filename), "%s_Autodump_%s.txt", p_ptr->name, datetime);
390
391     screen_save();
392     (void)file_character(creature_ptr, filename, display_player);
393     screen_load();
394 }
395
396 /*!
397  * @brief 死亡、引退時の簡易ステータス表示
398  * @param creature_ptr プレーヤーへの参照ポインタ
399  * @param display_player ステータス表示へのコールバック
400  * @return なし
401  */
402 void show_death_info(player_type *creature_ptr, display_player_pf display_player)
403 {
404     inventory_aware(creature_ptr);
405     home_aware(creature_ptr);
406
407     creature_ptr->update |= (PU_BONUS);
408     handle_stuff(creature_ptr);
409     flush();
410     msg_erase();
411
412     if (auto_dump)
413         file_character_auto(creature_ptr, display_player);
414
415     export_player_info(creature_ptr, display_player);
416     (*display_player)(creature_ptr, 0);
417     prt(_("何かキーを押すとさらに情報が続きます (ESCで中断): ", "Hit any key to see more information (ESC to abort): "), 23, 0);
418     if (inkey() == ESCAPE)
419         return;
420     if (show_dead_player_items(creature_ptr))
421         return;
422
423     show_dead_home_items(creature_ptr);
424 }