OSDN Git Service

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