1 #include "birth/auto-roller.h"
2 #include "birth/birth-stat.h"
3 #include "birth/birth-util.h"
4 #include "cmd-io/cmd-gameoption.h"
5 #include "io/input-key-acceptor.h"
6 #include "locale/japanese.h"
7 #include "main/sound-of-music.h"
8 #include "player-info/class-info.h"
9 #include "player-info/race-info.h"
10 #include "player/player-personality.h"
11 #include "player/player-sex.h"
12 #include "player/player-status-table.h"
13 #include "system/game-option-types.h"
14 #include "system/player-type-definition.h"
15 #include "term/screen-processor.h"
16 #include "term/term-color-types.h"
17 #include "term/z-form.h"
18 #include "util/int-char-converter.h"
20 /*! オートローラの能力値的要求水準 / Autoroll limit */
21 int16_t stat_limit[6];
23 /*! オートローラの試行回数 / Autoroll round */
25 int32_t auto_upper_round;
28 int32_t autoroll_chance;
31 * @breif オートローラーで指定した能力値以上が出る確率を計算する。
34 static int32_t get_autoroller_prob(int *minval)
36 /* 1 percent of the valid random space (60^6 && 72<sum<87) */
37 int32_t tot_rand_1p = 320669745;
46 /* random combinations out of 60 (1d3+1d4+1d5) patterns */
48 0, 0, 0, 0, 0, 0, 0, 0, /* 0-7 */
49 1, 3, 6, 9, 11, 11, 9, 6, 3, 1 /* 8-17 */
53 for (i = 0; i < 6; i++) {
54 tval[i] = std::max(8, minval[i]);
63 /* bubble sort for speed-up */
64 for (i = 0; i < 5; i++) {
65 for (j = 5; j > i; j--) {
66 if (tval[j - 1] < tval[j]) {
68 tval[j - 1] = tval[j];
77 for (ii[0] = tval[0]; ii[0] < 18; ii[0]++) {
78 for (ii[1] = tval[1]; ii[1] < 18; ii[1]++) {
79 for (ii[2] = tval[2]; ii[2] < 18; ii[2]++) {
80 for (ii[3] = tval[3]; ii[3] < 18; ii[3]++) {
81 for (ii[4] = tval[4]; ii[4] < 18; ii[4]++) {
82 for (ii[5] = tval[5]; ii[5] < 18; ii[5]++) {
83 tot = ii[0] + ii[1] + ii[2] + ii[3] + ii[4] + ii[5];
92 succ += (pp[ii[0]] * pp[ii[1]] * pp[ii[2]] * pp[ii[3]] * pp[ii[4]] * pp[ii[5]]);
94 /* If given condition is easy enough, quit calc. to save CPU. */
105 return tot_rand_1p / succ;
109 * @brief オートローラの初期設定値を決定する
110 * @param player_ptr プレイヤー情報への参照ポインタ
111 * @param cval 設定能力値配列
113 * 純戦士系及び腕器耐が魔法の能力の職業は腕器耐17。
114 * デュアルは腕耐17で器と魔法の能力が16。
115 * 純メイジ系は耐と魔法の能力が17で腕器16。
116 * デュアルかどうかは最大攻撃回数で決定。(4回以上)
118 static void decide_initial_stat(PlayerType *player_ptr, int *cval)
120 auto pclass = enum2i(player_ptr->pclass);
121 auto &class_ptr = class_info[pclass];
122 auto &magic_ptr = class_magics_info[pclass];
123 auto is_magic_user = magic_ptr.spell_stat == A_INT || magic_ptr.spell_stat == A_WIS || magic_ptr.spell_stat == A_CHR;
124 auto is_attacker = class_ptr.num > 3;
128 auto st = magic_ptr.spell_stat;
129 if (st >= 0 && st < A_MAX) {
139 if (cval[A_CON] == 0) {
146 if (cval[A_STR] == 0) {
147 cval[A_STR] = num_17 == 2 ? 16 : 17;
148 if (is_magic_user && num_17 < 2) {
153 if (cval[A_DEX] == 0) {
154 cval[A_DEX] = 17 - std::max(0, num_17 - 1);
157 for (int i = 0; i < A_MAX; i++) {
165 * @brief オートローラの設定能力値行を作成する
166 * @param cval 設定能力値配列
167 * @param cs カーソル位置(能力値番号)
170 static std::string cursor_of_adjusted_stat(const int *cval, int cs)
172 auto j = rp_ptr->r_adj[cs] + cp_ptr->c_adj[cs] + ap_ptr->a_adj[cs];
173 auto m = adjust_stat(17, j);
176 strnfmt(maxv, sizeof(maxv), "18/%02d", (m - 18));
178 strnfmt(maxv, sizeof(maxv), "%2d", m);
181 m = adjust_stat(cval[cs], j);
184 strnfmt(inp, sizeof(inp), "18/%02d", (m - 18));
186 strnfmt(inp, sizeof(inp), "%2d", m);
190 strnfmt(cur, sizeof(cur), "%6s %2d %+3d %+3d %+3d = %6s %6s", stat_names[cs], cval[cs], rp_ptr->r_adj[cs], cp_ptr->c_adj[cs], ap_ptr->a_adj[cs], inp, maxv);
195 * @brief オートローラの確率を表示
196 * @param cval 設定能力値配列
198 static void display_autoroller_chance(int *cval)
202 autoroll_chance = get_autoroller_prob(cval);
203 if (autoroll_chance == -999) {
204 buf = _("確率: 不可能(合計86超) ", "Prob: Impossible(>86 tot stats)");
205 } else if (autoroll_chance < 1) {
206 buf = _("確率: 非常に容易(1/10000以上)", "Prob: Quite Easy(>1/10000) ");
208 strnfmt(work, sizeof(work), _("確率: 約 1/%8d00 ", "Prob: ~ 1/%8d00 "), autoroll_chance);
211 put_str(buf, 23, 25);
215 * @brief オートローラで得たい能力値の基準を決める。
216 * @param player_ptr プレイヤーへの参照ポインタ
218 bool get_stat_limits(PlayerType *player_ptr)
221 put_str(_("能力値を抽選します。最低限得たい能力値を設定して下さい。", "Set minimum stats for picking up your charactor."), 10, 10);
222 put_str(_("2/8で項目選択、4/6で値の増減、Enterで次へ", "2/8 for Select, 4/6 for Change value, Enter for Goto next"), 11, 10);
223 put_str(_(" 基本値 種族 職業 性格 合計値 最大値", " Base Rac Cla Per Total Maximum"), 13, 10);
226 decide_initial_stat(player_ptr, cval);
228 for (int i = 0; i < A_MAX; i++) {
229 put_str(cursor_of_adjusted_stat(cval, i), 14 + i, 10);
232 display_autoroller_chance(cval);
240 display_autoroller_chance(cval);
241 } else if (os == A_MAX) {
242 c_put_str(TERM_WHITE, _("決定する", "Accept"), 21, 35);
243 } else if (os < A_MAX) {
244 c_put_str(TERM_WHITE, cur, 14 + os, 10);
248 c_put_str(TERM_YELLOW, _("決定する", "Accept"), 21, 35);
250 cur = cursor_of_adjusted_stat(cval, cs);
251 c_put_str(TERM_YELLOW, cur, 14 + cs, 10);
293 } else if (cval[cs] > 3) {
305 if (cval[cs] == 17) {
308 } else if (cval[cs] < 17) {
332 show_help(player_ptr, _("jbirth.txt#AutoRoller", "birth.txt#AutoRoller"));
336 do_cmd_options_aux(player_ptr, OPT_PAGE_BIRTH, _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
344 if (c == ESCAPE || ((c == ' ' || c == '\r' || c == '\n') && cs == 6 && autoroll_chance != -999)) {
349 for (int i = 0; i < A_MAX; i++) {
350 stat_limit[i] = (int16_t)cval[i];
356 void initialize_chara_limit(chara_limit_type *chara_limit_ptr)
358 chara_limit_ptr->agemin = 0;
359 chara_limit_ptr->agemax = 0;
360 chara_limit_ptr->htmin = 0;
361 chara_limit_ptr->htmax = 0;
362 chara_limit_ptr->wtmin = 0;
363 chara_limit_ptr->wtmax = 0;
364 chara_limit_ptr->scmin = 0;
365 chara_limit_ptr->scmax = 0;
369 * @brief オートローラで得たい年齢、身長、体重、社会的地位の基準を決める。
371 bool get_chara_limits(PlayerType *player_ptr, chara_limit_type *chara_limit_ptr)
375 concptr itemname[] = { _("年齢", "age"), _("身長(cm)", "height"), _("体重(kg)", "weight"), _("社会的地位", "social class") };
378 put_str(_("2/4/6/8で項目選択、+/-で値の増減、Enterで次へ", "2/4/6/8 for Select, +/- for Change value, Enter for Goto next"), 11, 10);
380 _("注意:身長と体重の最大値/最小値ぎりぎりの値は非常に出現確率が低くなります。", "Caution: Values near minimum or maximum are extremely rare."), 23, 2);
382 int max_percent, min_percent;
383 if (player_ptr->psex == SEX_MALE) {
384 max_percent = (int)(rp_ptr->m_b_ht + rp_ptr->m_m_ht * 4 - 1) * 100 / (int)(rp_ptr->m_b_ht);
385 min_percent = (int)(rp_ptr->m_b_ht - rp_ptr->m_m_ht * 4 + 1) * 100 / (int)(rp_ptr->m_b_ht);
387 max_percent = (int)(rp_ptr->f_b_ht + rp_ptr->f_m_ht * 4 - 1) * 100 / (int)(rp_ptr->f_b_ht);
388 min_percent = (int)(rp_ptr->f_b_ht - rp_ptr->f_m_ht * 4 + 1) * 100 / (int)(rp_ptr->f_b_ht);
391 put_str(_("体格/地位の最小値/最大値を設定して下さい。", "Set minimum/maximum attribute."), 10, 10);
392 put_str(_(" 項 目 最小値 最大値", " Parameter Min Max"), 13, 20);
395 for (int i = 0; i < MAXITEMS; i++) {
398 case 0: /* Minimum age */
399 m = rp_ptr->b_age + 1;
401 case 1: /* Maximum age */
402 m = rp_ptr->b_age + rp_ptr->m_age;
405 case 2: /* Minimum height */
406 if (player_ptr->psex == SEX_MALE) {
407 m = rp_ptr->m_b_ht - rp_ptr->m_m_ht * 4 + 1;
409 m = rp_ptr->f_b_ht - rp_ptr->f_m_ht * 4 + 1;
412 case 3: /* Maximum height */
413 if (player_ptr->psex == SEX_MALE) {
414 m = rp_ptr->m_b_ht + rp_ptr->m_m_ht * 4 - 1;
416 m = rp_ptr->f_b_ht + rp_ptr->f_m_ht * 4 - 1;
419 case 4: /* Minimum weight */
420 if (player_ptr->psex == SEX_MALE) {
421 m = (rp_ptr->m_b_wt * min_percent / 100) - (rp_ptr->m_m_wt * min_percent / 75) + 1;
423 m = (rp_ptr->f_b_wt * min_percent / 100) - (rp_ptr->f_m_wt * min_percent / 75) + 1;
426 case 5: /* Maximum weight */
427 if (player_ptr->psex == SEX_MALE) {
428 m = (rp_ptr->m_b_wt * max_percent / 100) + (rp_ptr->m_m_wt * max_percent / 75) - 1;
430 m = (rp_ptr->f_b_wt * max_percent / 100) + (rp_ptr->f_m_wt * max_percent / 75) - 1;
433 case 6: /* Minimum social class */
436 case 7: /* Maximum social class */
448 mval[2] = inch_to_cm(mval[2]);
449 mval[3] = inch_to_cm(mval[3]);
450 mval[4] = lb_to_kg(mval[4]);
451 mval[5] = lb_to_kg(mval[5]);
453 for (auto i = 0; i < MAXITEMS; i++) {
457 for (int i = 0; i < 4; i++) {
459 strnfmt(buf, sizeof(buf), "%-12s (%3d - %3d)", itemname[i], mval[i * 2], mval[i * 2 + 1]);
460 put_str(buf, 14 + i, 20);
461 for (int j = 0; j < 2; j++) {
462 strnfmt(buf, sizeof(buf), " %3d", cval[i * 2 + j]);
463 put_str(buf, 14 + i, 45 + 8 * j);
472 constexpr auto accept = _("決定する", "Accept");
473 if (os == MAXITEMS) {
474 c_put_str(TERM_WHITE, accept, 19, 35);
476 c_put_str(TERM_WHITE, cur, 14 + os / 2, 45 + 8 * (os % 2));
479 if (cs == MAXITEMS) {
480 c_put_str(TERM_YELLOW, accept, 19, 35);
482 strnfmt(cur, sizeof(cur), " %3d", cval[cs]);
483 c_put_str(TERM_YELLOW, cur, 14 + cs / 2, 45 + 8 * (cs % 2));
497 break; /*後でもう一回breakせんと*/
501 if (cs == MAXITEMS) {
536 if (cs != MAXITEMS) {
538 if (cval[cs] > cval[cs - 1]) {
543 if (cval[cs] > mval[cs]) {
553 if (cs != MAXITEMS) {
555 if (cval[cs] < mval[cs]) {
560 if (cval[cs] < cval[cs + 1]) {
569 if (cs != MAXITEMS) {
571 if (cval[cs] < mval[cs]) {
576 if (cval[cs] < cval[cs + 1]) {
577 cval[cs] = cval[cs + 1];
585 if (cs != MAXITEMS) {
587 if (cval[cs] > cval[cs - 1]) {
588 cval[cs] = cval[cs - 1];
592 if (cval[cs] > mval[cs]) {
602 show_help(player_ptr, "jbirth.txt#AutoRoller");
604 show_help(player_ptr, "birth.txt#AutoRoller");
609 do_cmd_options_aux(player_ptr, OPT_PAGE_BIRTH, _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
617 if (c == ESCAPE || ((c == ' ' || c == '\r' || c == '\n') && cs == MAXITEMS)) {
622 chara_limit_ptr->agemin = (int16_t)cval[0];
623 chara_limit_ptr->agemax = (int16_t)cval[1];
624 chara_limit_ptr->htmin = (int16_t)cval[2];
625 chara_limit_ptr->htmax = (int16_t)cval[3];
626 chara_limit_ptr->wtmin = (int16_t)cval[4];
627 chara_limit_ptr->wtmax = (int16_t)cval[5];
628 chara_limit_ptr->scmin = (int16_t)cval[6];
629 chara_limit_ptr->scmax = (int16_t)cval[7];