OSDN Git Service

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