OSDN Git Service

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