OSDN Git Service

[Refactor] #38862 Moved floor*.c to floor/
[hengband/hengband.git] / src / scores.c
1 /*!
2  * @file scores.c
3  * @brief ハイスコア処理 / Highscores handling
4  * @date 2014/07/14
5  * @author
6  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
7  * This software may be copied and distributed for educational, research,
8  * and not for profit purposes provided that this copyright and statement
9  * are included in all such copies.  Other copyrights may also apply.
10  * 2014 Deskull rearranged comment for Doxygen.
11  */
12
13 #include "angband.h"
14 #include "io/signal-handlers.h"
15 #include "gameterm.h"
16 #include "util.h"
17 #include "core/turn-compensator.h"
18 #include "core/angband-version.h"
19
20 #include "dungeon/dungeon.h"
21 #include "player/race-info-table.h"
22 #include "player-status.h"
23 #include "player-class.h"
24 #include "player-personality.h"
25 #include "player-sex.h"
26 #include "io/uid-checker.h"
27 #include "files.h"
28 #include "scores.h"
29 #include "floor/floor.h"
30 #include "world/world.h"
31 #include "io/write-diary.h"
32 #include "cmd/cmd-dump.h"
33 #include "report.h"
34 #include "japanese.h"
35
36  /*
37   * The "highscore" file descriptor, if available.
38   */
39 int highscore_fd = -1;
40
41 /*!
42  * @brief i番目のスコア情報にバッファ位置をシークする / Seek score 'i' in the highscore file
43  * @param i スコア情報ID
44  * @return 問題がなければ0を返す
45  */
46 static int highscore_seek(int i)
47 {
48         /* Seek for the requested record */
49         return (fd_seek(highscore_fd, (huge)(i) * sizeof(high_score)));
50 }
51
52
53 /*!
54  * @brief 所定ポインタからスコア情報を読み取る / Read one score from the highscore file
55  * @param score スコア情報参照ポインタ
56  * @return エラーコード
57  */
58 static errr highscore_read(high_score *score)
59 {
60         /* Read the record, note failure */
61         return (fd_read(highscore_fd, (char*)(score), sizeof(high_score)));
62 }
63
64
65 /*!
66  * @brief 所定ポインタへスコア情報を書き込む / Write one score to the highscore file
67  * @param score スコア情報参照ポインタ
68  * @return エラーコード(問題がなければ0を返す)
69  */
70 static int highscore_write(high_score *score)
71 {
72         /* Write the record, note failure */
73         return (fd_write(highscore_fd, (char*)(score), sizeof(high_score)));
74 }
75
76
77 /*!
78  * @brief スコア情報を全て得るまで繰り返し取得する / Just determine where a new score *would* be placed
79  * @param score スコア情報参照ポインタ
80  * @return 正常ならば(MAX_HISCORES - 1)、問題があれば-1を返す
81  */
82 static int highscore_where(high_score *score)
83 {
84         /* Paranoia -- it may not have opened */
85         if (highscore_fd < 0) return -1;
86
87         /* Go to the start of the highscore file */
88         if (highscore_seek(0)) return -1;
89
90         /* Read until we get to a higher score */
91         high_score the_score;
92         int my_score = atoi(score->pts);
93         for (int i = 0; i < MAX_HISCORES; i++)
94         {
95                 int old_score;
96                 if (highscore_read(&the_score)) return (i);
97                 old_score = atoi(the_score.pts);
98                 if (my_score > old_score) return (i);
99         }
100
101         /* The "last" entry is always usable */
102         return MAX_HISCORES - 1;
103 }
104
105
106 /*!
107  * @brief スコア情報をバッファの末尾に追加する / Actually place an entry into the high score file
108  * @param score スコア情報参照ポインタ
109  * @return 正常ならば書き込んだスロット位置、問題があれば-1を返す / Return the location (0 is best) or -1 on "failure"
110  */
111 static int highscore_add(high_score *score)
112 {
113         /* Paranoia -- it may not have opened */
114         if (highscore_fd < 0) return -1;
115
116         /* Determine where the score should go */
117         int slot = highscore_where(score);
118
119         /* Hack -- Not on the list */
120         if (slot < 0) return -1;
121
122         /* Hack -- prepare to dump the new score */
123         high_score the_score = (*score);
124
125         /* Slide all the scores down one */
126         bool done = FALSE;
127         high_score tmpscore;
128         for (int i = slot; !done && (i < MAX_HISCORES); i++)
129         {
130                 /* Read the old guy, note errors */
131                 if (highscore_seek(i)) return -1;
132                 if (highscore_read(&tmpscore)) done = TRUE;
133
134                 /* Back up and dump the score we were holding */
135                 if (highscore_seek(i)) return -1;
136                 if (highscore_write(&the_score)) return -1;
137
138                 /* Hack -- Save the old score, for the next pass */
139                 the_score = tmpscore;
140         }
141
142         /* Return location used */
143         return slot;
144 }
145
146
147 /*!
148  * @brief 指定された順位範囲でスコアを並べて表示する / Display the scores in a given range.
149  * @param from 順位先頭
150  * @param to 順位末尾
151  * @param note 黄色表示でハイライトする順位
152  * @param score スコア配列参照ポインタ
153  * @return なし
154  * @details
155  * <pre>
156  * Assumes the high score list is already open.
157  * Only five entries per line, too much info.
158  *
159  * Mega-Hack -- allow "fake" entry at the given position.
160  * </pre>
161  */
162 void display_scores_aux(int from, int to, int note, high_score *score)
163 {
164         int i, j, k, n, place;
165         TERM_COLOR attr;
166
167         high_score the_score;
168
169         GAME_TEXT out_val[256];
170         GAME_TEXT tmp_val[160];
171
172         TERM_LEN wid, hgt, per_screen;
173
174         Term_get_size(&wid, &hgt);
175         per_screen = (hgt - 4) / 4;
176
177         /* Paranoia -- it may not have opened */
178         if (highscore_fd < 0) return;
179
180
181         /* Assume we will show the first 10 */
182         if (from < 0) from = 0;
183         if (to < 0) to = 10;
184         if (to > MAX_HISCORES) to = MAX_HISCORES;
185
186
187         /* Seek to the beginning */
188         if (highscore_seek(0)) return;
189
190         /* Hack -- Count the high scores */
191         for (i = 0; i < MAX_HISCORES; i++)
192         {
193                 if (highscore_read(&the_score)) break;
194         }
195
196         /* Hack -- allow "fake" entry to be last */
197         if ((note == i) && score) i++;
198
199         /* Forget about the last entries */
200         if (i > to) i = to;
201
202
203         /* Show per_screen per page, until "done" */
204         for (k = from, place = k+1; k < i; k += per_screen)
205         {
206                 Term_clear();
207
208                 /* Title */
209                 put_str(_("                変愚蛮怒: 勇者の殿堂", "                Hengband Hall of Fame"), 0, 0);
210
211                 /* Indicate non-top scores */
212                 if (k > 0)
213                 {
214                         sprintf(tmp_val, _("( %d 位以下 )", "(from position %d)"), k + 1);
215                         put_str(tmp_val, 0, 40);
216                 }
217
218                 /* Dump per_screen entries */
219                 for (j = k, n = 0; j < i && n < per_screen; place++, j++, n++)
220                 {
221                         int pr, pc, pa, clev, mlev, cdun, mdun;
222
223                         concptr user, gold, when, aged;
224
225
226                         /* Hack -- indicate death in yellow */
227                         attr = (j == note) ? TERM_YELLOW : TERM_WHITE;
228
229
230                         /* Mega-Hack -- insert a "fake" record */
231                         if ((note == j) && score)
232                         {
233                                 the_score = (*score);
234                                 attr = TERM_L_GREEN;
235                                 score = NULL;
236                                 note = -1;
237                                 j--;
238                         }
239
240                         /* Read a normal record */
241                         else
242                         {
243                                 /* Read the proper record */
244                                 if (highscore_seek(j)) break;
245                                 if (highscore_read(&the_score)) break;
246                         }
247
248                         /* Extract the race/class */
249                         pr = atoi(the_score.p_r);
250                         pc = atoi(the_score.p_c);
251                         pa = atoi(the_score.p_a);
252
253                         /* Extract the level info */
254                         clev = atoi(the_score.cur_lev);
255                         mlev = atoi(the_score.max_lev);
256                         cdun = atoi(the_score.cur_dun);
257                         mdun = atoi(the_score.max_dun);
258
259                         /* Hack -- extract the gold and such */
260                         for (user = the_score.uid; iswspace(*user); user++) /* loop */;
261                         for (when = the_score.day; iswspace(*when); when++) /* loop */;
262                         for (gold = the_score.gold; iswspace(*gold); gold++) /* loop */;
263                         for (aged = the_score.turns; iswspace(*aged); aged++) /* loop */;
264
265                         /* Clean up standard encoded form of "when" */
266                         if ((*when == '@') && strlen(when) == 9)
267                         {
268                                 sprintf(tmp_val, "%.4s-%.2s-%.2s",
269                                         when + 1, when + 5, when + 7);
270                                 when = tmp_val;
271                         }
272
273                         /* Dump some info */
274 #ifdef JP
275 /*sprintf(out_val, "%3d.%9s  %s%s%sという名の%sの%s (レベル %d)", */
276                         sprintf(out_val, "%3d.%9s  %s%s%s - %s%s (レベル %d)",
277                                 place, the_score.pts,
278                                 seikaku_info[pa].title, (seikaku_info[pa].no ? "の" : ""),
279                                 the_score.who,
280                                 race_info[pr].title, class_info[pc].title,
281                                 clev);
282
283 #else
284                         sprintf(out_val, "%3d.%9s  %s %s the %s %s, Level %d",
285                                 place, the_score.pts,
286                                 seikaku_info[pa].title,
287                                 the_score.who, race_info[pr].title, class_info[pc].title,
288                                 clev);
289 #endif
290
291
292                         /* Append a "maximum level" */
293                         if (mlev > clev) strcat(out_val, format(_(" (最高%d)", " (Max %d)"), mlev));
294
295                         /* Dump the first line */
296                         c_put_str(attr, out_val, n*4 + 2, 0);
297
298                         /* Another line of info */
299 #ifdef JP
300                         if (mdun != 0)
301                                 sprintf(out_val, "    最高%3d階", mdun);
302                         else
303                                 sprintf(out_val, "             ");
304
305
306                         /* 死亡原因をオリジナルより細かく表示 */
307                         if (streq(the_score.how, "yet"))
308                         {
309                                 sprintf(out_val+13, "  まだ生きている (%d%s)",
310                                        cdun, "階");
311                         }
312                         else
313                         if (streq(the_score.how, "ripe"))
314                         {
315                                 sprintf(out_val+13, "  勝利の後に引退 (%d%s)",
316                                         cdun, "階");
317                         }
318                         else if (streq(the_score.how, "Seppuku"))
319                         {
320                                 sprintf(out_val+13, "  勝利の後に切腹 (%d%s)",
321                                         cdun, "階");
322                         }
323                         else
324                         {
325                                 codeconv(the_score.how);
326
327                                 /* Some people die outside of the dungeon */
328                                 if (!cdun)
329                                         sprintf(out_val+13, "  地上で%sに殺された", the_score.how);
330                                 else
331                                         sprintf(out_val+13, "  %d階で%sに殺された",
332                                                 cdun, the_score.how);
333                         }
334
335 #else
336                         /* Some people die outside of the dungeon */
337                         if (!cdun)
338                                 sprintf(out_val, 
339                                         "               Killed by %s on the surface",
340                                         the_score.how);
341                         else
342                                 sprintf(out_val, 
343                                         "               Killed by %s on %s %d",
344                                         the_score.how, "Dungeon Level", cdun);
345
346                         /* Append a "maximum level" */
347                         if (mdun > cdun) strcat(out_val, format(" (Max %d)", mdun));
348 #endif
349
350                         /* Dump the info */
351                         c_put_str(attr, out_val, n*4 + 3, 0);
352
353                         /* And still another line of info */
354 #ifdef JP
355                         {
356                                 char buf[11];
357
358                                 /* 日付を 19yy/mm/dd の形式に変更する */
359                                 if (strlen(when) == 8 && when[2] == '/' && when[5] == '/') {
360                                         sprintf(buf, "%d%s/%.5s", 19 + (when[6] < '8'), when + 6, when);
361                                         when = buf;
362                                 }
363                                 sprintf(out_val,
364                                                 "        (ユーザー:%s, 日付:%s, 所持金:%s, ターン:%s)",
365                                                 user, when, gold, aged);
366                         }
367
368 #else
369                         sprintf(out_val,
370                                 "               (User %s, Date %s, Gold %s, Turn %s).",
371                                 user, when, gold, aged);
372 #endif
373
374                         c_put_str(attr, out_val, n*4 + 4, 0);
375                 }
376
377
378                 /* Wait for response */
379                 prt(_("[ ESCで中断, その他のキーで続けます ]", "[Press ESC to quit, any other key to continue.]"), hgt - 1, _(21, 17));
380
381                 j = inkey();
382                 prt("", hgt - 1, 0);
383
384                 /* Hack -- notice Escape */
385                 if (j == ESCAPE) break;
386         }
387 }
388
389
390 /*!
391  * @brief スコア表示処理メインルーチン / Hack -- Display the scores in a given range and quit.
392  * @param from 順位先頭
393  * @param to 順位末尾
394  * @return なし
395  * @details
396  * <pre>
397  * This function is only called from "main.c" when the user asks
398  * to see the "high scores".
399  * </pre>
400  */
401 void display_scores(int from, int to)
402 {
403         char buf[1024];
404         path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw");
405
406         /* Open the binary high score file, for reading */
407         highscore_fd = fd_open(buf, O_RDONLY);
408
409         /* Paranoia -- No score file */
410         if (highscore_fd < 0) quit(_("スコア・ファイルが使用できません。", "Score file unavailable."));
411         Term_clear();
412
413         /* Display the scores */
414         display_scores_aux(from, to, -1, NULL);
415
416         /* Shut the high score file */
417         (void)fd_close(highscore_fd);
418
419         /* Forget the high score fd */
420         highscore_fd = -1;
421
422         /* Quit */
423         quit(NULL);
424 }
425
426
427 /*!
428  * todo プリプロが邪魔していて最初のif文を削除すると到達不能コードが発生する
429  * @brief スコアサーバへの転送処理
430  * @param current_player_ptr プレーヤーへの参照ポインタ
431  * @param do_send 実際に転送ア処置を行うか否か
432  * @return 転送が成功したらTRUEを返す
433  */
434 bool send_world_score(player_type *current_player_ptr, bool do_send, void(*update_playtime)(void), display_player_pf display_player, map_name_pf map_name)
435 {
436 #ifdef WORLD_SCORE
437         if (send_score && do_send)
438         {
439                 if (easy_band)
440                 {
441                         msg_print(_("初心者モードではワールドスコアに登録できません。",
442                                 "Since you are in the Easy Mode, you cannot send score to world score server."));
443                         return TRUE;
444                 }
445                 
446                 bool is_registration = get_check_strict(_("スコアをスコア・サーバに登録しますか? ", "Do you send score to the world score server? "), (CHECK_NO_ESCAPE | CHECK_NO_HISTORY));
447                 if (!is_registration) return FALSE;
448
449                 errr err;
450                 prt("", 0, 0);
451                 prt(_("送信中..", "Sending..."), 0, 0);
452                 Term_fresh();
453                 screen_save();
454                 err = report_score(current_player_ptr, update_playtime, display_player, map_name);
455                 screen_load();
456                 if (err) return FALSE;
457
458                 prt(_("完了。何かキーを押してください。", "Completed.  Hit any key."), 0, 0);
459                 (void)inkey();
460         }
461 #endif
462         return TRUE;
463 }
464
465
466 /*!
467  * @brief スコアの過去二十位内ランキングを表示する
468  * Enters a players name on a hi-score table, if "legal", and in any
469  * case, displays some relevant portion of the high score list.
470  * @param current_player_ptr スコアに適用するための現在プレイヤークリーチャー参照ポインタ
471  * @return エラーコード
472  * @details
473  * Assumes "signals_ignore_tstp()" has been called.
474  */
475 errr top_twenty(player_type *current_player_ptr)
476 {
477         high_score the_score;
478         (void)WIPE(&the_score, high_score);
479
480         /* Save the version */
481         sprintf(the_score.what, "%u.%u.%u",
482                 FAKE_VER_MAJOR, FAKE_VER_MINOR, FAKE_VER_PATCH);
483
484         /* Calculate and save the points */
485         sprintf(the_score.pts, "%9ld", (long)calc_score(current_player_ptr));
486         the_score.pts[9] = '\0';
487
488         /* Save the current gold */
489         sprintf(the_score.gold, "%9lu", (long)current_player_ptr->au);
490         the_score.gold[9] = '\0';
491
492         /* Save the current turn */
493         sprintf(the_score.turns, "%9lu", (long)turn_real(current_player_ptr, current_world_ptr->game_turn));
494         the_score.turns[9] = '\0';
495
496         time_t ct = time((time_t*)0);
497
498         /* Save the date in standard encoded form (9 chars) */
499         strftime(the_score.day, 10, "@%Y%m%d", localtime(&ct));
500
501         /* Save the player name (15 chars) */
502         sprintf(the_score.who, "%-.15s", current_player_ptr->name);
503
504         /* Save the player info */
505         sprintf(the_score.uid, "%7u", current_player_ptr->player_uid);
506         sprintf(the_score.sex, "%c", (current_player_ptr->psex ? 'm' : 'f'));
507         sprintf(the_score.p_r, "%2d", MIN(current_player_ptr->prace, MAX_RACES));
508         sprintf(the_score.p_c, "%2d", MIN(current_player_ptr->pclass, MAX_CLASS));
509         sprintf(the_score.p_a, "%2d", MIN(current_player_ptr->pseikaku, MAX_SEIKAKU));
510
511         /* Save the level and such */
512         sprintf(the_score.cur_lev, "%3d", MIN((u16b)current_player_ptr->lev, 999));
513         sprintf(the_score.cur_dun, "%3d", (int)current_player_ptr->current_floor_ptr->dun_level);
514         sprintf(the_score.max_lev, "%3d", MIN((u16b)current_player_ptr->max_plv, 999));
515         sprintf(the_score.max_dun, "%3d", (int)max_dlv[current_player_ptr->dungeon_idx]);
516
517         /* Save the cause of death (31 chars) */
518         if (strlen(current_player_ptr->died_from) >= sizeof(the_score.how))
519         {
520 #ifdef JP
521                 my_strcpy(the_score.how, current_player_ptr->died_from, sizeof(the_score.how) - 2);
522                 strcat(the_score.how, "…");
523 #else
524                 my_strcpy(the_score.how, current_player_ptr->died_from, sizeof(the_score.how) - 3);
525                 strcat(the_score.how, "...");
526 #endif
527         }
528         else
529         {
530                 strcpy(the_score.how, current_player_ptr->died_from);
531         }
532
533         /* Grab permissions */
534         safe_setuid_grab();
535
536         /* Lock (for writing) the highscore file, or fail */
537         errr err = fd_lock(highscore_fd, F_WRLCK);
538
539         /* Drop permissions */
540         safe_setuid_drop();
541
542         if (err) return 1;
543
544         /* Add a new entry to the score list, see where it went */
545         int j = highscore_add(&the_score);
546
547         /* Grab permissions */
548         safe_setuid_grab();
549
550         /* Unlock the highscore file, or fail */
551         err = fd_lock(highscore_fd, F_UNLCK);
552
553         /* Drop permissions */
554         safe_setuid_drop();
555
556         if (err) return 1;
557
558         /* Hack -- Display the top fifteen scores */
559         if (j < 10)
560         {
561                 display_scores_aux(0, 15, j, NULL);
562                 return 0;
563         }
564
565         /* Display the scores surrounding the player */
566         display_scores_aux(0, 5, j, NULL);
567         display_scores_aux(j - 2, j + 7, j, NULL);
568         return 0;
569 }
570
571
572 /*!
573  * @brief プレイヤーの現在のスコアをランキングに挟む /
574  * Predict the players location, and display it.
575  * @return エラーコード
576  */
577 errr predict_score(player_type *current_player_ptr)
578 {
579         high_score the_score;
580
581         /* No score file */
582         if (highscore_fd < 0)
583         {
584                 msg_print(_("スコア・ファイルが使用できません。", "Score file unavailable."));
585                 msg_print(NULL);
586                 return 0;
587         }
588
589         /* Save the version */
590         sprintf(the_score.what, "%u.%u.%u",
591                 FAKE_VER_MAJOR, FAKE_VER_MINOR, FAKE_VER_PATCH);
592
593         /* Calculate and save the points */
594         sprintf(the_score.pts, "%9ld", (long)calc_score(current_player_ptr));
595
596         /* Save the current gold */
597         sprintf(the_score.gold, "%9lu", (long)current_player_ptr->au);
598
599         /* Save the current turn */
600         sprintf(the_score.turns, "%9lu", (long)turn_real(current_player_ptr, current_world_ptr->game_turn));
601
602         /* Hack -- no time needed */
603         strcpy(the_score.day, _("今日", "TODAY"));
604
605         /* Save the player name (15 chars) */
606         sprintf(the_score.who, "%-.15s", current_player_ptr->name);
607
608         /* Save the player info */
609         sprintf(the_score.uid, "%7u", current_player_ptr->player_uid);
610         sprintf(the_score.sex, "%c", (current_player_ptr->psex ? 'm' : 'f'));
611         sprintf(the_score.p_r, "%2d", MIN(current_player_ptr->prace, MAX_RACES));
612         sprintf(the_score.p_c, "%2d", MIN(current_player_ptr->pclass, MAX_CLASS));
613         sprintf(the_score.p_a, "%2d", MIN(current_player_ptr->pseikaku, MAX_SEIKAKU));
614
615         /* Save the level and such */
616         sprintf(the_score.cur_lev, "%3d", MIN((u16b)current_player_ptr->lev, 999));
617         sprintf(the_score.cur_dun, "%3d", (int)current_player_ptr->current_floor_ptr->dun_level);
618         sprintf(the_score.max_lev, "%3d", MIN((u16b)current_player_ptr->max_plv, 999));
619         sprintf(the_score.max_dun, "%3d", (int)max_dlv[current_player_ptr->dungeon_idx]);
620
621         /* Hack -- no cause of death */
622         /* まだ死んでいないときの識別文字 */
623         strcpy(the_score.how, _("yet", "nobody (yet!)"));
624
625         /* See where the entry would be placed */
626         int j = highscore_where(&the_score);
627
628         /* Hack -- Display the top fifteen scores */
629         if (j < 10)
630         {
631                 display_scores_aux(0, 15, j, &the_score);
632                 return 0;
633         }
634
635         display_scores_aux(0, 5, -1, NULL);
636         display_scores_aux(j - 2, j + 7, j, &the_score);
637         return 0;
638 }
639
640
641 /*!
642  * @brief スコアランキングの簡易表示 /
643  * show_highclass - selectively list highscores based on class -KMW-
644  * @return なし
645  */
646 void show_highclass(player_type *current_player_ptr)
647 {
648         screen_save();
649         char buf[1024], out_val[256];
650         path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw");
651
652         highscore_fd = fd_open(buf, O_RDONLY);
653
654         if (highscore_fd < 0)
655         {
656                 msg_print(_("スコア・ファイルが使用できません。", "Score file unavailable."));
657                 msg_print(NULL);
658                 return;
659         }
660
661         if (highscore_seek(0)) return;
662
663         high_score the_score;
664         for (int i = 0; i < MAX_HISCORES; i++)
665                 if (highscore_read(&the_score)) break;
666
667         int m = 0;
668         int j = 0;
669         PLAYER_LEVEL clev = 0;
670         int pr;
671         while ((m < 9) && (j < MAX_HISCORES))
672         {
673                 if (highscore_seek(j)) break;
674                 if (highscore_read(&the_score)) break;
675                 pr = atoi(the_score.p_r);
676                 clev = (PLAYER_LEVEL)atoi(the_score.cur_lev);
677
678 #ifdef JP
679                 sprintf(out_val, "   %3d) %sの%s (レベル %2d)",
680                     (m + 1), race_info[pr].title,the_score.who, clev);
681 #else
682                 sprintf(out_val, "%3d) %s the %s (Level %2d)",
683                     (m + 1), the_score.who, race_info[pr].title, clev);
684 #endif
685
686                 prt(out_val, (m + 7), 0);
687                 m++;
688                 j++;
689         }
690
691 #ifdef JP
692         sprintf(out_val, "あなた) %sの%s (レベル %2d)",
693             race_info[current_player_ptr->prace].title,current_player_ptr->name, current_player_ptr->lev);
694 #else
695         sprintf(out_val, "You) %s the %s (Level %2d)",
696             current_player_ptr->name, race_info[current_player_ptr->prace].title, current_player_ptr->lev);
697 #endif
698
699         prt(out_val, (m + 8), 0);
700
701         (void)fd_close(highscore_fd);
702         highscore_fd = -1;
703         prt(_("何かキーを押すとゲームに戻ります", "Hit any key to continue"),0,0);
704
705         (void)inkey();
706
707         for (j = 5; j < 18; j++) prt("", j, 0);
708         screen_load();
709 }
710
711
712 /*!
713  * @brief スコアランキングの簡易表示(種族毎)サブルーチン /
714  * Race Legends -KMW-
715  * @param race_num 種族ID
716  * @return なし
717  */
718 void race_score(player_type *current_player_ptr, int race_num)
719 {
720         register int i = 0, j, m = 0;
721         int pr, clev, lastlev;
722         high_score the_score;
723         char buf[1024], out_val[256], tmp_str[80];
724
725         lastlev = 0;
726
727         /* rr9: TODO - pluralize the race */
728         sprintf(tmp_str,_("最高の%s", "The Greatest of all the %s"), race_info[race_num].title);
729
730         prt(tmp_str, 5, 15);
731         path_build(buf, sizeof(buf), ANGBAND_DIR_APEX, "scores.raw");
732
733         highscore_fd = fd_open(buf, O_RDONLY);
734
735         if (highscore_fd < 0)
736         {
737                 msg_print(_("スコア・ファイルが使用できません。", "Score file unavailable."));
738                 msg_print(NULL);
739                 return;
740         }
741
742         if (highscore_seek(0)) return;
743
744         for (i = 0; i < MAX_HISCORES; i++)
745         {
746                 if (highscore_read(&the_score)) break;
747         }
748
749         m = 0;
750         j = 0;
751
752         while ((m < 10) || (j < MAX_HISCORES))
753         {
754                 if (highscore_seek(j)) break;
755                 if (highscore_read(&the_score)) break;
756                 pr = atoi(the_score.p_r);
757                 clev = atoi(the_score.cur_lev);
758
759                 if (pr == race_num)
760                 {
761 #ifdef JP
762                 sprintf(out_val, "   %3d) %sの%s (レベル %2d)",
763                             (m + 1), race_info[pr].title, 
764                                 the_score.who,clev);
765 #else
766                         sprintf(out_val, "%3d) %s the %s (Level %3d)",
767                             (m + 1), the_score.who,
768                         race_info[pr].title, clev);
769 #endif
770
771                         prt(out_val, (m + 7), 0);
772                         m++;
773                         lastlev = clev;
774                 }
775                 j++;
776         }
777
778         /* add player if qualified */
779         if ((current_player_ptr->prace == race_num) && (current_player_ptr->lev >= lastlev))
780         {
781 #ifdef JP
782         sprintf(out_val, "あなた) %sの%s (レベル %2d)",
783                      race_info[current_player_ptr->prace].title,current_player_ptr->name, current_player_ptr->lev);
784 #else
785                 sprintf(out_val, "You) %s the %s (Level %3d)",
786                     current_player_ptr->name, race_info[current_player_ptr->prace].title, current_player_ptr->lev);
787 #endif
788
789                 prt(out_val, (m + 8), 0);
790         }
791
792         (void)fd_close(highscore_fd);
793         highscore_fd = -1;
794 }
795
796
797 /*!
798  * @brief スコアランキングの簡易表示(種族毎)メインルーチン /
799  * Race Legends -KMW-
800  * @return なし
801  */
802 void race_legends(player_type *current_player_ptr)
803 {
804         for (int i = 0; i < MAX_RACES; i++)
805         {
806                 race_score(current_player_ptr, i);
807                 msg_print(_("何かキーを押すとゲームに戻ります", "Hit any key to continue"));
808                 msg_print(NULL);
809                 for (int j = 5; j < 19; j++)
810                         prt("", j, 0);
811         }
812 }
813
814
815 /*!
816  * @brief 勝利者用の引退演出処理 /
817  * Change the player into a King! -RAK-
818  * @return なし
819  */
820 void kingly(player_type *winner_ptr)
821 {
822         TERM_LEN wid, hgt;
823         TERM_LEN cx, cy;
824         bool seppuku = streq(winner_ptr->died_from, "Seppuku");
825
826         /* Hack -- retire in town */
827         winner_ptr->current_floor_ptr->dun_level = 0;
828
829         /* Fake death */
830         if (!seppuku)
831                 /* 引退したときの識別文字 */
832                 (void)strcpy(winner_ptr->died_from, _("ripe", "Ripe Old Age"));
833
834         /* Restore the experience */
835         winner_ptr->exp = winner_ptr->max_exp;
836
837         /* Restore the level */
838         winner_ptr->lev = winner_ptr->max_plv;
839
840         Term_get_size(&wid, &hgt);
841         cy = hgt / 2;
842         cx = wid / 2;
843
844         /* Hack -- Instant Gold */
845         winner_ptr->au += 10000000L;
846         Term_clear();
847
848         /* Display a crown */
849         put_str("#", cy - 11, cx - 1);
850         put_str("#####", cy - 10, cx - 3);
851         put_str("#", cy - 9, cx - 1);
852         put_str(",,,  $$$  ,,,", cy - 8, cx - 7);
853         put_str(",,=$   \"$$$$$\"   $=,,", cy - 7, cx - 11);
854         put_str(",$$        $$$        $$,", cy - 6, cx - 13);
855         put_str("*>         <*>         <*", cy - 5, cx - 13);
856         put_str("$$         $$$         $$", cy - 4, cx - 13);
857         put_str("\"$$        $$$        $$\"", cy - 3, cx - 13);
858         put_str("\"$$       $$$       $$\"", cy - 2, cx - 12);
859         put_str("*#########*#########*", cy - 1, cx - 11);
860         put_str("*#########*#########*", cy, cx - 11);
861
862         /* Display a message */
863 #ifdef JP
864         put_str("Veni, Vidi, Vici!", cy + 3, cx - 9);
865         put_str("来た、見た、勝った!", cy + 4, cx - 10);
866         put_str(format("偉大なる%s万歳!", sp_ptr->winner), cy + 5, cx - 11);
867 #else
868         put_str("Veni, Vidi, Vici!", cy + 3, cx - 9);
869         put_str("I came, I saw, I conquered!", cy + 4, cx - 14);
870         put_str(format("All Hail the Mighty %s!", sp_ptr->winner), cy + 5, cx - 13);
871 #endif
872
873         /* If player did Seppuku, that is already written in playrecord */
874         if (!seppuku)
875         {
876                 exe_write_diary(winner_ptr, DIARY_DESCRIPTION, 0, _("ダンジョンの探索から引退した。", "retired exploring dungeons."));
877                 exe_write_diary(winner_ptr, DIARY_GAMESTART, 1, _("-------- ゲームオーバー --------", "--------   Game  Over   --------"));
878                 exe_write_diary(winner_ptr, DIARY_DESCRIPTION, 1, "\n\n\n\n");
879         }
880
881         /* Flush input */
882         flush();
883
884         /* Wait for response */
885         pause_line(hgt - 1);
886 }
887
888
889 /*!
890  * @brief スコアファイル出力
891  * Display some character info
892  * @return なし
893  */
894 bool check_score(player_type *current_player_ptr)
895 {
896         Term_clear();
897
898         /* No score file */
899         if (highscore_fd < 0)
900         {
901                 msg_print(_("スコア・ファイルが使用できません。", "Score file unavailable."));
902                 msg_print(NULL);
903                 return FALSE;
904         }
905
906         /* Wizard-mode pre-empts scoring */
907         if (current_world_ptr->noscore & 0x000F)
908         {
909                 msg_print(_("ウィザード・モードではスコアが記録されません。", "Score not registered for wizards."));
910                 msg_print(NULL);
911                 return FALSE;
912         }
913
914         /* Cheaters are not scored */
915         if (current_world_ptr->noscore & 0xFF00)
916         {
917                 msg_print(_("詐欺をやった人はスコアが記録されません。", "Score not registered for cheaters."));
918                 msg_print(NULL);
919                 return FALSE;
920         }
921
922         /* Interupted */
923         if (!current_world_ptr->total_winner && streq(current_player_ptr->died_from, _("強制終了", "Interrupting")))
924         {
925                 msg_print(_("強制終了のためスコアが記録されません。", "Score not registered due to interruption."));
926                 msg_print(NULL);
927                 return FALSE;
928         }
929
930         /* Quitter */
931         if (!current_world_ptr->total_winner && streq(current_player_ptr->died_from, _("途中終了", "Quitting")))
932         {
933                 msg_print(_("途中終了のためスコアが記録されません。", "Score not registered due to quitting."));
934                 msg_print(NULL);
935                 return FALSE;
936         }
937         return TRUE;
938 }