OSDN Git Service

Merge pull request #3569 from sikabane-works/release/3.0.0.88-alpha
[hengbandforosx/hengbandosx.git] / src / birth / auto-roller.cpp
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"
19
20 /*! オートローラの能力値的要求水準 / Autoroll limit */
21 int16_t stat_limit[6];
22
23 /*! オートローラの試行回数 / Autoroll round */
24 int32_t auto_round;
25 int32_t auto_upper_round;
26
27 /*! オートローラの要求値実現確率 */
28 int32_t autoroll_chance;
29
30 /*!
31  * @breif オートローラーで指定した能力値以上が出る確率を計算する。
32  * @return 確率 / 100
33  */
34 static int32_t get_autoroller_prob(int *minval)
35 {
36     /* 1 percent of the valid random space (60^6 && 72<sum<87) */
37     int32_t tot_rand_1p = 320669745;
38     int i, j, tmp;
39     int ii[6];
40     int tval[6];
41     int tot = 0;
42
43     /* success count */
44     int32_t succ = 0;
45
46     /* random combinations out of 60 (1d3+1d4+1d5) patterns */
47     int pp[18] = {
48         0, 0, 0, 0, 0, 0, 0, 0, /* 0-7 */
49         1, 3, 6, 9, 11, 11, 9, 6, 3, 1 /* 8-17 */
50     };
51
52     /* Copy */
53     for (i = 0; i < 6; i++) {
54         tval[i] = std::max(8, minval[i]);
55         tot += tval[i];
56     }
57
58     /* No Chance */
59     if (tot > 86) {
60         return -999;
61     }
62
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]) {
67                 tmp = tval[j - 1];
68                 tval[j - 1] = tval[j];
69                 tval[j] = tmp;
70             }
71         }
72     }
73
74     tot = 0;
75
76     /* calc. prob. */
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];
84
85                             if (tot > 86) {
86                                 break;
87                             }
88                             if (tot <= 72) {
89                                 continue;
90                             }
91
92                             succ += (pp[ii[0]] * pp[ii[1]] * pp[ii[2]] * pp[ii[3]] * pp[ii[4]] * pp[ii[5]]);
93
94                             /* If given condition is easy enough, quit calc. to save CPU. */
95                             if (succ > 320670) {
96                                 return -1;
97                             }
98                         }
99                     }
100                 }
101             }
102         }
103     }
104
105     return tot_rand_1p / succ;
106 }
107
108 /*!
109  * @brief オートローラの初期設定値を決定する
110  * @param player_ptr プレイヤー情報への参照ポインタ
111  * @param cval 設定能力値配列
112  * @details
113  * 純戦士系及び腕器耐が魔法の能力の職業は腕器耐17。
114  * デュアルは腕耐17で器と魔法の能力が16。
115  * 純メイジ系は耐と魔法の能力が17で腕器16。
116  * デュアルかどうかは最大攻撃回数で決定。(4回以上)
117  */
118 static void decide_initial_stat(PlayerType *player_ptr, int *cval)
119 {
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;
125
126     auto num_17 = 0;
127     if (is_magic_user) {
128         auto st = magic_ptr.spell_stat;
129         if (st >= 0 && st < A_MAX) {
130             if (is_attacker) {
131                 cval[st] = 16;
132             } else {
133                 cval[st] = 17;
134                 num_17++;
135             }
136         }
137     }
138
139     if (cval[A_CON] == 0) {
140         cval[A_CON] = 17;
141         if (is_magic_user) {
142             num_17++;
143         }
144     }
145
146     if (cval[A_STR] == 0) {
147         cval[A_STR] = num_17 == 2 ? 16 : 17;
148         if (is_magic_user && num_17 < 2) {
149             num_17++;
150         }
151     }
152
153     if (cval[A_DEX] == 0) {
154         cval[A_DEX] = 17 - std::max(0, num_17 - 1);
155     }
156
157     for (int i = 0; i < A_MAX; i++) {
158         if (cval[i] == 0) {
159             cval[i] = 8;
160         }
161     }
162 }
163
164 /*!
165  * @brief オートローラの設定能力値行を作成する
166  * @param cval 設定能力値配列
167  * @param cs カーソル位置(能力値番号)
168  * @return カーソル文字列
169  */
170 static std::string cursor_of_adjusted_stat(const int *cval, int cs)
171 {
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);
174     char maxv[20];
175     if (m > 18) {
176         strnfmt(maxv, sizeof(maxv), "18/%02d", (m - 18));
177     } else {
178         strnfmt(maxv, sizeof(maxv), "%2d", m);
179     }
180
181     m = adjust_stat(cval[cs], j);
182     char inp[20];
183     if (m > 18) {
184         strnfmt(inp, sizeof(inp), "18/%02d", (m - 18));
185     } else {
186         strnfmt(inp, sizeof(inp), "%2d", m);
187     }
188
189     char cur[60];
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);
191     return cur;
192 }
193
194 /*!
195  * @brief オートローラの確率を表示
196  * @param cval 設定能力値配列
197  */
198 static void display_autoroller_chance(int *cval)
199 {
200     concptr buf;
201     char work[60];
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)     ");
207     } else {
208         strnfmt(work, sizeof(work), _("確率: 約 1/%8d00             ", "Prob: ~ 1/%8d00                "), autoroll_chance);
209         buf = work;
210     }
211     put_str(buf, 23, 25);
212 }
213
214 /*!
215  * @brief オートローラで得たい能力値の基準を決める。
216  * @param player_ptr プレイヤーへの参照ポインタ
217  */
218 bool get_stat_limits(PlayerType *player_ptr)
219 {
220     clear_from(10);
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);
224
225     int cval[A_MAX]{};
226     decide_initial_stat(player_ptr, cval);
227
228     for (int i = 0; i < A_MAX; i++) {
229         put_str(cursor_of_adjusted_stat(cval, i), 14 + i, 10);
230     }
231
232     display_autoroller_chance(cval);
233
234     std::string cur;
235     int cs = 0;
236     int os = A_MAX;
237     while (true) {
238         if (cs != os) {
239             if (os == 7) {
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);
245             }
246
247             if (cs == A_MAX) {
248                 c_put_str(TERM_YELLOW, _("決定する", "Accept"), 21, 35);
249             } else {
250                 cur = cursor_of_adjusted_stat(cval, cs);
251                 c_put_str(TERM_YELLOW, cur, 14 + cs, 10);
252             }
253
254             os = cs;
255         }
256
257         char c = inkey();
258         switch (c) {
259         case 'Q':
260             birth_quit();
261             break;
262         case 'S':
263             return false;
264         case ESCAPE:
265             break;
266         case ' ':
267         case '\r':
268         case '\n':
269             if (cs == A_MAX) {
270                 break;
271             }
272             cs++;
273             c = '2';
274             break;
275         case '8':
276         case 'k':
277             if (cs > 0) {
278                 cs--;
279             }
280             break;
281         case '2':
282         case 'j':
283             if (cs < A_MAX) {
284                 cs++;
285             }
286             break;
287         case '4':
288         case 'h':
289             if (cs != A_MAX) {
290                 if (cval[cs] == 3) {
291                     cval[cs] = 17;
292                     os = 7;
293                 } else if (cval[cs] > 3) {
294                     cval[cs]--;
295                     os = 7;
296                 } else {
297                     return false;
298                 }
299             }
300
301             break;
302         case '6':
303         case 'l':
304             if (cs != A_MAX) {
305                 if (cval[cs] == 17) {
306                     cval[cs] = 3;
307                     os = 7;
308                 } else if (cval[cs] < 17) {
309                     cval[cs]++;
310                     os = 7;
311                 } else {
312                     return false;
313                 }
314             }
315
316             break;
317         case 'm':
318             if (cs != A_MAX) {
319                 cval[cs] = 17;
320                 os = 7;
321             }
322
323             break;
324         case 'n':
325             if (cs != A_MAX) {
326                 cval[cs] = 3;
327                 os = 7;
328             }
329
330             break;
331         case '?':
332             show_help(player_ptr, _("jbirth.txt#AutoRoller", "birth.txt#AutoRoller"));
333             break;
334         case '=':
335             screen_save();
336             do_cmd_options_aux(player_ptr, OPT_PAGE_BIRTH, _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
337             screen_load();
338             break;
339         default:
340             bell();
341             break;
342         }
343
344         if (c == ESCAPE || ((c == ' ' || c == '\r' || c == '\n') && cs == 6 && autoroll_chance != -999)) {
345             break;
346         }
347     }
348
349     for (int i = 0; i < A_MAX; i++) {
350         stat_limit[i] = (int16_t)cval[i];
351     }
352
353     return true;
354 }
355
356 void initialize_chara_limit(chara_limit_type *chara_limit_ptr)
357 {
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;
366 }
367
368 /*!
369  * @brief オートローラで得たい年齢、身長、体重、社会的地位の基準を決める。
370  */
371 bool get_chara_limits(PlayerType *player_ptr, chara_limit_type *chara_limit_ptr)
372 {
373 #define MAXITEMS 8
374
375     concptr itemname[] = { _("年齢", "age"), _("身長(cm)", "height"), _("体重(kg)", "weight"), _("社会的地位", "social class") };
376
377     clear_from(10);
378     put_str(_("2/4/6/8で項目選択、+/-で値の増減、Enterで次へ", "2/4/6/8 for Select, +/- for Change value, Enter for Goto next"), 11, 10);
379     put_str(
380         _("注意:身長と体重の最大値/最小値ぎりぎりの値は非常に出現確率が低くなります。", "Caution: Values near minimum or maximum are extremely rare."), 23, 2);
381
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);
386     } else {
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);
389     }
390
391     put_str(_("体格/地位の最小値/最大値を設定して下さい。", "Set minimum/maximum attribute."), 10, 10);
392     put_str(_("  項    目                 最小値  最大値", " Parameter                    Min     Max"), 13, 20);
393     int mval[MAXITEMS];
394     int cval[MAXITEMS];
395     for (int i = 0; i < MAXITEMS; i++) {
396         int m;
397         switch (i) {
398         case 0: /* Minimum age */
399             m = rp_ptr->b_age + 1;
400             break;
401         case 1: /* Maximum age */
402             m = rp_ptr->b_age + rp_ptr->m_age;
403             break;
404
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;
408             } else {
409                 m = rp_ptr->f_b_ht - rp_ptr->f_m_ht * 4 + 1;
410             }
411             break;
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;
415             } else {
416                 m = rp_ptr->f_b_ht + rp_ptr->f_m_ht * 4 - 1;
417             }
418             break;
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;
422             } else {
423                 m = (rp_ptr->f_b_wt * min_percent / 100) - (rp_ptr->f_m_wt * min_percent / 75) + 1;
424             }
425             break;
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;
429             } else {
430                 m = (rp_ptr->f_b_wt * max_percent / 100) + (rp_ptr->f_m_wt * max_percent / 75) - 1;
431             }
432             break;
433         case 6: /* Minimum social class */
434             m = 1;
435             break;
436         case 7: /* Maximum social class */
437             m = 100;
438             break;
439         default:
440             m = 1;
441             break;
442         }
443
444         mval[i] = m;
445     }
446 #ifdef JP
447     /*身長と体重の単位をcmとkgに*/
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]);
452 #endif
453     for (auto i = 0; i < MAXITEMS; i++) {
454         cval[i] = mval[i];
455     }
456
457     for (int i = 0; i < 4; i++) {
458         char buf[40];
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);
464         }
465     }
466
467     char cur[40] = "";
468     int cs = 0;
469     int os = MAXITEMS;
470     while (true) {
471         if (cs != os) {
472             constexpr auto accept = _("決定する", "Accept");
473             if (os == MAXITEMS) {
474                 c_put_str(TERM_WHITE, accept, 19, 35);
475             } else {
476                 c_put_str(TERM_WHITE, cur, 14 + os / 2, 45 + 8 * (os % 2));
477             }
478
479             if (cs == MAXITEMS) {
480                 c_put_str(TERM_YELLOW, accept, 19, 35);
481             } else {
482                 strnfmt(cur, sizeof(cur), "     %3d", cval[cs]);
483                 c_put_str(TERM_YELLOW, cur, 14 + cs / 2, 45 + 8 * (cs % 2));
484             }
485
486             os = cs;
487         }
488
489         char c = inkey();
490         switch (c) {
491         case 'Q':
492             birth_quit();
493             break;
494         case 'S':
495             return false;
496         case ESCAPE:
497             break; /*後でもう一回breakせんと*/
498         case ' ':
499         case '\r':
500         case '\n':
501             if (cs == MAXITEMS) {
502                 break;
503             }
504             cs++;
505             c = '6';
506             break;
507         case '8':
508         case 'k':
509             if (cs - 2 >= 0) {
510                 cs -= 2;
511             }
512             break;
513         case '2':
514         case 'j':
515             if (cs < MAXITEMS) {
516                 cs += 2;
517             }
518             if (cs > MAXITEMS) {
519                 cs = MAXITEMS;
520             }
521             break;
522         case '4':
523         case 'h':
524             if (cs > 0) {
525                 cs--;
526             }
527             break;
528         case '6':
529         case 'l':
530             if (cs < MAXITEMS) {
531                 cs++;
532             }
533             break;
534         case '-':
535         case '<':
536             if (cs != MAXITEMS) {
537                 if (cs % 2) {
538                     if (cval[cs] > cval[cs - 1]) {
539                         cval[cs]--;
540                         os = 127;
541                     }
542                 } else {
543                     if (cval[cs] > mval[cs]) {
544                         cval[cs]--;
545                         os = 127;
546                     }
547                 }
548             }
549
550             break;
551         case '+':
552         case '>':
553             if (cs != MAXITEMS) {
554                 if (cs % 2) {
555                     if (cval[cs] < mval[cs]) {
556                         cval[cs]++;
557                         os = 127;
558                     }
559                 } else {
560                     if (cval[cs] < cval[cs + 1]) {
561                         cval[cs]++;
562                         os = 127;
563                     }
564                 }
565             }
566
567             break;
568         case 'm':
569             if (cs != MAXITEMS) {
570                 if (cs % 2) {
571                     if (cval[cs] < mval[cs]) {
572                         cval[cs] = mval[cs];
573                         os = 127;
574                     }
575                 } else {
576                     if (cval[cs] < cval[cs + 1]) {
577                         cval[cs] = cval[cs + 1];
578                         os = 127;
579                     }
580                 }
581             }
582
583             break;
584         case 'n':
585             if (cs != MAXITEMS) {
586                 if (cs % 2) {
587                     if (cval[cs] > cval[cs - 1]) {
588                         cval[cs] = cval[cs - 1];
589                         os = 255;
590                     }
591                 } else {
592                     if (cval[cs] > mval[cs]) {
593                         cval[cs] = mval[cs];
594                         os = 255;
595                     }
596                 }
597             }
598
599             break;
600         case '?':
601 #ifdef JP
602             show_help(player_ptr, "jbirth.txt#AutoRoller");
603 #else
604             show_help(player_ptr, "birth.txt#AutoRoller");
605 #endif
606             break;
607         case '=':
608             screen_save();
609             do_cmd_options_aux(player_ptr, OPT_PAGE_BIRTH, _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
610             screen_load();
611             break;
612         default:
613             bell();
614             break;
615         }
616
617         if (c == ESCAPE || ((c == ' ' || c == '\r' || c == '\n') && cs == MAXITEMS)) {
618             break;
619         }
620     }
621
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];
630     return true;
631 }