3 * @brief ハイスコア処理 / Highscores handling
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.
13 #include "core/scores.h"
14 #include "cmd-io/cmd-dump.h"
15 #include "core/asking-player.h"
16 #include "core/score-util.h"
17 #include "core/turn-compensator.h"
18 #include "game-option/birth-options.h"
19 #include "game-option/game-play-options.h"
20 #include "io/files-util.h"
21 #include "io/input-key-acceptor.h"
22 #include "io/report.h"
23 #include "io/signal-handlers.h"
24 #include "io/uid-checker.h"
25 #include "locale/japanese.h"
26 #include "player-base/player-race.h"
27 #include "player-info/class-info.h"
28 #include "player/player-personality.h"
29 #include "player/player-status.h"
30 #include "player/race-info-table.h"
31 #include "system/angband-version.h"
32 #include "system/dungeon-info.h"
33 #include "system/floor-type-definition.h"
34 #include "system/player-type-definition.h"
35 #include "term/screen-processor.h"
36 #include "term/term-color-types.h"
37 #include "util/angband-files.h"
38 #include "util/enum-converter.h"
39 #include "util/int-char-converter.h"
40 #include "util/string-processor.h"
41 #include "view/display-messages.h"
42 #include "view/display-scores.h"
43 #include "world/world.h"
46 * @brief 所定ポインタへスコア情報を書き込む / Write one score to the highscore file
47 * @param score スコア情報参照ポインタ
48 * @return エラーコード(問題がなければ0を返す)
50 static int highscore_write(high_score *score)
52 /* Write the record, note failure */
53 return fd_write(highscore_fd, (char *)(score), sizeof(high_score));
57 * @brief スコア情報を全て得るまで繰り返し取得する / Just determine where a new score *would* be placed
58 * @param score スコア情報参照ポインタ
59 * @return 正常ならば(MAX_HISCORES - 1)、問題があれば-1を返す
61 static int highscore_where(high_score *score)
63 /* Paranoia -- it may not have opened */
64 if (highscore_fd < 0) {
68 /* Go to the start of the highscore file */
69 if (highscore_seek(0)) {
73 /* Read until we get to a higher score */
75 int my_score = atoi(score->pts);
76 for (int i = 0; i < MAX_HISCORES; i++) {
78 if (highscore_read(&the_score)) {
81 old_score = atoi(the_score.pts);
82 if (my_score > old_score) {
87 /* The "last" entry is always usable */
88 return MAX_HISCORES - 1;
92 * @brief スコア情報をバッファの末尾に追加する / Actually place an entry into the high score file
93 * @param score スコア情報参照ポインタ
94 * @return 正常ならば書き込んだスロット位置、問題があれば-1を返す / Return the location (0 is best) or -1 on "failure"
96 static int highscore_add(high_score *score)
98 /* Paranoia -- it may not have opened */
99 if (highscore_fd < 0) {
103 /* Determine where the score should go */
104 int slot = highscore_where(score);
106 /* Hack -- Not on the list */
111 /* Hack -- prepare to dump the new score */
112 high_score the_score = (*score);
114 /* Slide all the scores down one */
117 for (int i = slot; !done && (i < MAX_HISCORES); i++) {
118 /* Read the old guy, note errors */
119 if (highscore_seek(i)) {
122 if (highscore_read(&tmpscore)) {
126 /* Back up and dump the score we were holding */
127 if (highscore_seek(i)) {
130 if (highscore_write(&the_score)) {
134 /* Hack -- Save the old score, for the next pass */
135 the_score = tmpscore;
138 /* Return location used */
143 * @brief スコアサーバへの転送処理
144 * @param player_ptr プレイヤーへの参照ポインタ
145 * @param do_send 実際に転送ア処置を行うか否か
146 * @return 転送が成功したらTRUEを返す
148 bool send_world_score(PlayerType *player_ptr, bool do_send)
151 if (!send_score || !do_send) {
155 auto is_registration = get_check_strict(
156 player_ptr, _("スコアをスコア・サーバに登録しますか? ", "Do you send score to the world score server? "), (CHECK_NO_ESCAPE | CHECK_NO_HISTORY));
157 if (!is_registration) {
162 prt(_("送信中..", "Sending..."), 0, 0);
165 auto successful_send = report_score(player_ptr);
167 if (!successful_send) {
171 prt(_("完了。何かキーを押してください。", "Completed. Hit any key."), 0, 0);
181 * @brief スコアの過去二十位内ランキングを表示する
182 * Enters a players name on a hi-score table, if "legal", and in any
183 * case, displays some relevant portion of the high score list.
184 * @param player_ptr プレイヤーへの参照ポインタ
187 * Assumes "signals_ignore_tstp()" has been called.
189 errr top_twenty(PlayerType *player_ptr)
191 high_score the_score = {};
194 /* Save the version */
195 snprintf(the_score.what, sizeof(the_score.what), "%u.%u.%u", H_VER_MAJOR, H_VER_MINOR, H_VER_PATCH);
197 /* Calculate and save the points */
198 snprintf(the_score.pts, sizeof(the_score.pts), "%9ld", (long)calc_score(player_ptr));
199 the_score.pts[9] = '\0';
201 /* Save the current gold */
202 snprintf(the_score.gold, sizeof(the_score.gold), "%9lu", (long)player_ptr->au);
203 the_score.gold[9] = '\0';
205 /* Save the current turn */
206 snprintf(the_score.turns, sizeof(the_score.turns), "%9lu", (long)turn_real(player_ptr, w_ptr->game_turn));
207 the_score.turns[9] = '\0';
209 time_t ct = time((time_t *)0);
211 /* Save the date in standard encoded form (9 chars) */
212 strftime(the_score.day, 10, "@%Y%m%d", localtime(&ct));
214 /* Save the player name (15 chars) */
215 snprintf(the_score.who, sizeof(the_score.who), "%-.15s", player_ptr->name);
217 /* Save the player info */
218 snprintf(the_score.uid, sizeof(the_score.uid), "%7u", player_ptr->player_uid);
219 snprintf(the_score.sex, sizeof(the_score.sex), "%c", (player_ptr->psex ? 'm' : 'f'));
220 snprintf(buf, sizeof(buf), "%2d", std::min(enum2i(player_ptr->prace), MAX_RACES));
221 memcpy(the_score.p_r, buf, 3);
222 snprintf(buf, sizeof(buf), "%2d", enum2i(std::min(player_ptr->pclass, PlayerClassType::MAX)));
223 memcpy(the_score.p_c, buf, 3);
224 snprintf(buf, sizeof(buf), "%2d", std::min(player_ptr->ppersonality, MAX_PERSONALITIES));
225 memcpy(the_score.p_a, buf, 3);
227 /* Save the level and such */
228 snprintf(the_score.cur_lev, sizeof(the_score.cur_lev), "%3d", std::min<ushort>(player_ptr->lev, 999));
229 snprintf(the_score.cur_dun, sizeof(the_score.cur_dun), "%3d", (int)player_ptr->current_floor_ptr->dun_level);
230 snprintf(the_score.max_lev, sizeof(the_score.max_lev), "%3d", std::min<ushort>(player_ptr->max_plv, 999));
231 snprintf(the_score.max_dun, sizeof(the_score.max_dun), "%3d", (int)max_dlv[player_ptr->dungeon_idx]);
233 /* Save the cause of death (31 chars) */
234 if (player_ptr->died_from.size() >= sizeof(the_score.how)) {
236 angband_strcpy(the_score.how, player_ptr->died_from.data(), sizeof(the_score.how) - 2);
237 angband_strcat(the_score.how, "…", sizeof(the_score.how));
239 angband_strcpy(the_score.how, player_ptr->died_from.data(), sizeof(the_score.how) - 3);
240 angband_strcat(the_score.how, "...", sizeof(the_score.how));
243 angband_strcpy(the_score.how, player_ptr->died_from.data(), sizeof(the_score.how));
246 /* Grab permissions */
247 safe_setuid_grab(player_ptr);
249 /* Lock (for writing) the highscore file, or fail */
250 errr err = fd_lock(highscore_fd, F_WRLCK);
252 /* Drop permissions */
259 /* Add a new entry to the score list, see where it went */
260 int j = highscore_add(&the_score);
262 /* Grab permissions */
263 safe_setuid_grab(player_ptr);
265 /* Unlock the highscore file, or fail */
266 err = fd_lock(highscore_fd, F_UNLCK);
268 /* Drop permissions */
275 /* Hack -- Display the top fifteen scores */
277 display_scores(0, 15, j, nullptr);
281 /* Display the scores surrounding the player */
282 display_scores(0, 5, j, nullptr);
283 display_scores(j - 2, j + 7, j, nullptr);
288 * @brief プレイヤーの現在のスコアをランキングに挟む /
289 * Predict the players location, and display it.
292 errr predict_score(PlayerType *player_ptr)
294 high_score the_score;
298 if (highscore_fd < 0) {
299 msg_print(_("スコア・ファイルが使用できません。", "Score file unavailable."));
304 /* Save the version */
305 snprintf(the_score.what, sizeof(the_score.what), "%u.%u.%u", H_VER_MAJOR, H_VER_MINOR, H_VER_PATCH);
307 /* Calculate and save the points */
308 snprintf(the_score.pts, sizeof(the_score.pts), "%9ld", (long)calc_score(player_ptr));
310 /* Save the current gold */
311 snprintf(the_score.gold, sizeof(the_score.gold), "%9lu", (long)player_ptr->au);
313 /* Save the current turn */
314 snprintf(the_score.turns, sizeof(the_score.turns), "%9lu", (long)turn_real(player_ptr, w_ptr->game_turn));
316 /* Hack -- no time needed */
317 angband_strcpy(the_score.day, _("今日", "TODAY"), sizeof(the_score.day));
319 /* Save the player name (15 chars) */
320 snprintf(the_score.who, sizeof(the_score.who), "%-.15s", player_ptr->name);
322 /* Save the player info */
323 snprintf(the_score.uid, sizeof(the_score.uid), "%7u", player_ptr->player_uid);
324 snprintf(the_score.sex, sizeof(the_score.sex), "%c", (player_ptr->psex ? 'm' : 'f'));
325 snprintf(buf, sizeof(buf), "%2d", std::min(enum2i(player_ptr->prace), MAX_RACES));
326 memcpy(the_score.p_r, buf, 3);
327 snprintf(buf, sizeof(buf), "%2d", enum2i(std::min(player_ptr->pclass, PlayerClassType::MAX)));
328 memcpy(the_score.p_c, buf, 3);
329 snprintf(buf, sizeof(buf), "%2d", std::min(player_ptr->ppersonality, MAX_PERSONALITIES));
330 memcpy(the_score.p_a, buf, 3);
332 /* Save the level and such */
333 snprintf(the_score.cur_lev, sizeof(the_score.cur_lev), "%3d", std::min<ushort>(player_ptr->lev, 999));
334 snprintf(the_score.cur_dun, sizeof(the_score.cur_dun), "%3d", (int)player_ptr->current_floor_ptr->dun_level);
335 snprintf(the_score.max_lev, sizeof(the_score.max_lev), "%3d", std::min<ushort>(player_ptr->max_plv, 999));
336 snprintf(the_score.max_dun, sizeof(the_score.max_dun), "%3d", (int)max_dlv[player_ptr->dungeon_idx]);
338 /* まだ死んでいないときの識別文字 */
339 strcpy(the_score.how, _("yet", "nobody (yet!)"));
341 /* See where the entry would be placed */
342 int j = highscore_where(&the_score);
344 /* Hack -- Display the top fifteen scores */
346 display_scores(0, 15, j, &the_score);
350 display_scores(0, 5, -1, nullptr);
351 display_scores(j - 2, j + 7, j, &the_score);
356 * @brief スコアランキングの簡易表示 /
357 * show_highclass - selectively list highscores based on class -KMW-
359 void show_highclass(PlayerType *player_ptr)
362 const auto &path = path_build(ANGBAND_DIR_APEX, "scores.raw");
363 highscore_fd = fd_open(path, O_RDONLY);
364 if (highscore_fd < 0) {
365 msg_print(_("スコア・ファイルが使用できません。", "Score file unavailable."));
370 if (highscore_seek(0)) {
374 high_score the_score;
375 for (int i = 0; i < MAX_HISCORES; i++) {
376 if (highscore_read(&the_score)) {
383 PLAYER_LEVEL clev = 0;
386 while ((m < 9) && (j < MAX_HISCORES)) {
387 if (highscore_seek(j)) {
390 if (highscore_read(&the_score)) {
393 pr = atoi(the_score.p_r);
394 clev = (PLAYER_LEVEL)atoi(the_score.cur_lev);
397 snprintf(out_val, sizeof(out_val), " %3d) %sの%s (レベル %2d)", (m + 1), race_info[pr].title, the_score.who, clev);
399 snprintf(out_val, sizeof(out_val), "%3d) %s the %s (Level %2d)", (m + 1), the_score.who, race_info[pr].title, clev);
402 prt(out_val, (m + 7), 0);
408 snprintf(out_val, sizeof(out_val), "あなた) %sの%s (レベル %2d)", race_info[enum2i(player_ptr->prace)].title, player_ptr->name, player_ptr->lev);
410 snprintf(out_val, sizeof(out_val), "You) %s the %s (Level %2d)", player_ptr->name, race_info[enum2i(player_ptr->prace)].title, player_ptr->lev);
413 prt(out_val, (m + 8), 0);
415 (void)fd_close(highscore_fd);
417 prt(_("何かキーを押すとゲームに戻ります", "Hit any key to continue"), 0, 0);
421 for (j = 5; j < 18; j++) {
428 * @brief スコアランキングの簡易表示(種族毎)サブルーチン /
430 * @param race_num 種族ID
432 void race_score(PlayerType *player_ptr, int race_num)
436 high_score the_score;
439 /* rr9: TODO - pluralize the race */
440 prt(std::string(_("最高の", "The Greatest of all the ")).append(race_info[race_num].title), 5, 15);
441 const auto &path = path_build(ANGBAND_DIR_APEX, "scores.raw");
442 highscore_fd = fd_open(path, O_RDONLY);
443 if (highscore_fd < 0) {
444 msg_print(_("スコア・ファイルが使用できません。", "Score file unavailable."));
449 if (highscore_seek(0)) {
453 for (i = 0; i < MAX_HISCORES; i++) {
454 if (highscore_read(&the_score)) {
462 while ((m < 10) || (j < MAX_HISCORES)) {
463 if (highscore_seek(j)) {
466 if (highscore_read(&the_score)) {
469 pr = atoi(the_score.p_r);
470 clev = atoi(the_score.cur_lev);
472 if (pr == race_num) {
475 snprintf(out_val, sizeof(out_val), " %3d) %sの%s (レベル %2d)", (m + 1), race_info[pr].title, the_score.who, clev);
477 snprintf(out_val, sizeof(out_val), "%3d) %s the %s (Level %3d)", (m + 1), the_score.who, race_info[pr].title, clev);
480 prt(out_val, (m + 7), 0);
487 /* add player if qualified */
488 if ((enum2i(player_ptr->prace) == race_num) && (player_ptr->lev >= lastlev)) {
491 snprintf(out_val, sizeof(out_val), "あなた) %sの%s (レベル %2d)", race_info[enum2i(player_ptr->prace)].title, player_ptr->name, player_ptr->lev);
493 snprintf(out_val, sizeof(out_val), "You) %s the %s (Level %3d)", player_ptr->name, race_info[enum2i(player_ptr->prace)].title, player_ptr->lev);
496 prt(out_val, (m + 8), 0);
499 (void)fd_close(highscore_fd);
504 * @brief スコアランキングの簡易表示(種族毎)メインルーチン /
507 void race_legends(PlayerType *player_ptr)
509 for (int i = 0; i < MAX_RACES; i++) {
510 race_score(player_ptr, i);
511 msg_print(_("何かキーを押すとゲームに戻ります", "Hit any key to continue"));
513 for (int j = 5; j < 19; j++) {
521 * Display some character info
523 bool check_score(PlayerType *player_ptr)
528 if (highscore_fd < 0) {
529 msg_print(_("スコア・ファイルが使用できません。", "Score file unavailable."));
534 /* Wizard-mode pre-empts scoring */
535 if (w_ptr->noscore & 0x000F) {
536 msg_print(_("ウィザード・モードではスコアが記録されません。", "Score not registered for wizards."));
541 /* Cheaters are not scored */
542 if (w_ptr->noscore & 0xFF00) {
543 msg_print(_("詐欺をやった人はスコアが記録されません。", "Score not registered for cheaters."));
549 if (!w_ptr->total_winner && streq(player_ptr->died_from, _("強制終了", "Interrupting"))) {
550 msg_print(_("強制終了のためスコアが記録されません。", "Score not registered due to interruption."));
556 if (!w_ptr->total_winner && streq(player_ptr->died_from, _("途中終了", "Quitting"))) {
557 msg_print(_("途中終了のためスコアが記録されません。", "Score not registered due to quitting."));