OSDN Git Service

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