OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / birth / birth-wizard.cpp
1 #include "birth/birth-wizard.h"
2 #include "avatar/avatar.h"
3 #include "birth/auto-roller.h"
4 #include "birth/birth-body-spec.h"
5 #include "birth/birth-explanations-table.h"
6 #include "birth/birth-select-class.h"
7 #include "birth/birth-select-personality.h"
8 #include "birth/birth-select-race.h"
9 #include "birth/birth-select-realm.h"
10 #include "birth/birth-stat.h"
11 #include "birth/birth-util.h"
12 #include "birth/game-play-initializer.h"
13 #include "birth/history-editor.h"
14 #include "birth/history-generator.h"
15 #include "birth/quick-start.h"
16 #include "cmd-io/cmd-gameoption.h"
17 #include "cmd-io/cmd-help.h"
18 #include "core/asking-player.h"
19 #include "game-option/birth-options.h"
20 #include "io/input-key-acceptor.h"
21 #include "locale/japanese.h"
22 #include "main/sound-definitions-table.h"
23 #include "main/sound-of-music.h"
24 #include "player-base/player-class.h"
25 #include "player-info/class-info.h"
26 #include "player-info/race-info.h"
27 #include "player/patron.h"
28 #include "player/player-personality.h"
29 #include "player/player-sex.h"
30 #include "player/player-status-table.h"
31 #include "player/player-status.h"
32 #include "player/process-name.h"
33 #include "system/game-option-types.h"
34 #include "system/player-type-definition.h"
35 #include "system/redrawing-flags-updater.h"
36 #include "term/screen-processor.h"
37 #include "term/term-color-types.h"
38 #include "term/z-form.h"
39 #include "util/enum-converter.h"
40 #include "util/int-char-converter.h"
41 #include "util/string-processor.h"
42 #include "view/display-birth.h" // 暫定。後で消す予定。
43 #include "view/display-player-misc-info.h"
44 #include "view/display-player.h" // 暫定。後で消す.
45 #include "view/display-util.h"
46 #include "world/world.h"
47 #include <sstream>
48
49 /*!
50  * オートローラーの内容を描画する間隔 /
51  * How often the autoroller will update the display and pause
52  * to check for user interuptions.
53  * Bigger values will make the autoroller faster, but slower
54  * system may have problems because the user can't stop the
55  * autoroller for this number of rolls.
56  */
57 #define AUTOROLLER_STEP 54321L
58
59 static void display_initial_birth_message(PlayerType *player_ptr)
60 {
61     term_clear();
62     display_player_name(player_ptr, true);
63     put_str(_("性別        :", "Sex         :"), 3, 1);
64     put_str(_("種族        :", "Race        :"), 4, 1);
65     put_str(_("職業        :", "Class       :"), 5, 1);
66     put_str(_("キャラクターを作成します。('S'やり直す, 'Q'終了, '?'ヘルプ)", "Make your character. ('S' Restart, 'Q' Quit, '?' Help)"), 8, 10);
67     put_str(_("注意:《性別》の違いはゲーム上ほとんど影響を及ぼしません。", "Note: Your 'sex' does not have any significant gameplay effects."), 23, 5);
68 }
69
70 /*!
71  * @prief 性別選択画面でヘルプを表示させる
72  * @param player_ptr プレイヤーへの参照ポインタ
73  * @param c 入力したコマンド
74  * @details 他の関数名と被りそうだったので少し眺め
75  */
76 static void display_help_on_sex_select(PlayerType *player_ptr, char c)
77 {
78     if (c == '?') {
79         do_cmd_help(player_ptr);
80     } else if (c == '=') {
81         screen_save();
82         do_cmd_options_aux(player_ptr, OPT_PAGE_BIRTH, _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
83         screen_load();
84     } else if (c != '4' && c != '6') {
85         bell();
86     }
87 }
88
89 /*!
90  * @brief 表示されている性別のラベルを取得する
91  * @param cs 性別の指標
92  * @return std::string 表示するラベルを保持
93  */
94 static std::string birth_sex_label(int cs)
95 {
96     const char p2 = ')';
97     std::stringstream ss;
98
99     if (cs < 0 || cs >= MAX_SEXES) {
100         ss << '*' << p2 << _("ランダム", "Random");
101     } else {
102         ss << I2A(cs) << p2 << sex_info[cs].title;
103     }
104     return ss.str();
105 }
106
107 /*!
108  * @brief プレイヤーの性別選択を行う / Player sex
109  * @param player_ptr プレイヤーへの参照ポインタ
110  * @return やり直すならFALSE、それ以外はTRUE
111  */
112 static bool get_player_sex(PlayerType *player_ptr)
113 {
114     int k = -1;
115     int cs = 0;
116     int os = MAX_SEXES;
117     auto cur = birth_sex_label(os);
118     while (true) {
119         if (cs != os) {
120             put_str(cur, 12 + (os / 5), 2 + 15 * (os % 5));
121             cur = birth_sex_label(cs);
122             c_put_str(TERM_YELLOW, cur, 12 + (cs / 5), 2 + 15 * (cs % 5));
123             os = cs;
124         }
125
126         if (k >= 0) {
127             break;
128         }
129
130         char buf[80];
131         strnfmt(buf, sizeof(buf), _("性別を選んで下さい (%c-%c) ('='初期オプション設定): ", "Choose a sex (%c-%c) ('=' for options): "), I2A(0), I2A(1));
132         put_str(buf, 10, 10);
133         char c = inkey();
134         if (c == 'Q') {
135             birth_quit();
136         }
137
138         if (c == 'S') {
139             return false;
140         }
141
142         if (c == ' ' || c == '\r' || c == '\n') {
143             k = cs == MAX_SEXES ? randint0(MAX_SEXES) : cs;
144             break;
145         }
146
147         if (c == '*') {
148             k = randint0(MAX_SEXES);
149             break;
150         }
151
152         if (c == '4') {
153             if (cs > 0) {
154                 cs--;
155             }
156         }
157
158         if (c == '6') {
159             if (cs < MAX_SEXES) {
160                 cs++;
161             }
162         }
163
164         k = (islower(c) ? A2I(c) : -1);
165         if ((k >= 0) && (k < MAX_SEXES)) {
166             cs = k;
167             continue;
168         } else {
169             k = -1;
170         }
171
172         display_help_on_sex_select(player_ptr, c);
173     }
174
175     player_ptr->psex = i2enum<player_sex>(k);
176     sp_ptr = &sex_info[player_ptr->psex];
177     c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 15);
178     return true;
179 }
180
181 static bool let_player_select_race(PlayerType *player_ptr)
182 {
183     clear_from(10);
184     player_ptr->prace = PlayerRaceType::HUMAN;
185     while (true) {
186         if (!get_player_race(player_ptr)) {
187             return false;
188         }
189
190         clear_from(10);
191         display_wrap_around(race_explanations[enum2i(player_ptr->prace)], 74, 12, 3);
192         if (input_check_strict(player_ptr, _("よろしいですか?", "Are you sure? "), UserCheck::DEFAULT_Y)) {
193             break;
194         }
195
196         clear_from(10);
197         c_put_str(TERM_WHITE, "              ", 4, 15);
198     }
199
200     return true;
201 }
202
203 static bool let_player_select_class(PlayerType *player_ptr)
204 {
205     clear_from(10);
206     player_ptr->pclass = PlayerClassType::WARRIOR;
207     while (true) {
208         if (!get_player_class(player_ptr)) {
209             return false;
210         }
211
212         clear_from(10);
213         display_wrap_around(class_explanations[enum2i(player_ptr->pclass)], 74, 12, 3);
214
215         if (input_check_strict(player_ptr, _("よろしいですか?", "Are you sure? "), UserCheck::DEFAULT_Y)) {
216             break;
217         }
218
219         c_put_str(TERM_WHITE, "              ", 5, 15);
220     }
221
222     return true;
223 }
224
225 static bool let_player_select_personality(PlayerType *player_ptr)
226 {
227     player_ptr->ppersonality = PERSONALITY_ORDINARY;
228     while (true) {
229         if (!get_player_personality(player_ptr)) {
230             return false;
231         }
232
233         clear_from(10);
234         display_wrap_around(personality_explanations[player_ptr->ppersonality], 74, 12, 3);
235
236         if (input_check_strict(player_ptr, _("よろしいですか?", "Are you sure? "), UserCheck::DEFAULT_Y)) {
237             break;
238         }
239
240         display_player_name(player_ptr, true);
241     }
242
243     return true;
244 }
245
246 static bool let_player_build_character(PlayerType *player_ptr)
247 {
248     if (!get_player_sex(player_ptr)) {
249         return false;
250     }
251
252     if (!let_player_select_race(player_ptr)) {
253         return false;
254     }
255
256     if (!let_player_select_class(player_ptr)) {
257         return false;
258     }
259
260     if (!get_player_realms(player_ptr)) {
261         return false;
262     }
263
264     if (!let_player_select_personality(player_ptr)) {
265         return false;
266     }
267
268     PlayerClass(player_ptr).init_specific_data();
269
270     return true;
271 }
272
273 static void display_initial_options(PlayerType *player_ptr)
274 {
275     uint16_t expfact = get_expfact(player_ptr) - 100;
276     int16_t adj[A_MAX];
277     for (int i = 0; i < A_MAX; i++) {
278         adj[i] = rp_ptr->r_adj[i] + cp_ptr->c_adj[i] + ap_ptr->a_adj[i];
279     }
280
281     char buf[80];
282     put_str("                                   ", 3, 40);
283     put_str(_("修正の合計値", "Your total modification"), 3, 40);
284     put_str(_("腕力 知能 賢さ 器用 耐久 魅力 経験 ", "Str  Int  Wis  Dex  Con  Chr   EXP "), 4, 40);
285     strnfmt(buf, sizeof(buf), "%+3d  %+3d  %+3d  %+3d  %+3d  %+3d %+4d%% ", adj[0], adj[1], adj[2], adj[3], adj[4], adj[5], expfact);
286     c_put_str(TERM_L_BLUE, buf, 5, 40);
287
288     put_str("HD ", 6, 40);
289     strnfmt(buf, sizeof(buf), "%2d", rp_ptr->r_mhp + cp_ptr->c_mhp + ap_ptr->a_mhp);
290     c_put_str(TERM_L_BLUE, buf, 6, 43);
291
292     put_str(_("隠密", "Stealth"), 6, 47);
293     if (PlayerClass(player_ptr).equals(PlayerClassType::BERSERKER)) {
294         angband_strcpy(buf, "xx", sizeof(buf));
295     } else {
296         strnfmt(buf, sizeof(buf), "%+2d", rp_ptr->r_stl + cp_ptr->c_stl + ap_ptr->a_stl);
297     }
298     c_put_str(TERM_L_BLUE, buf, 6, _(52, 55));
299
300     put_str(_("赤外線視力", "Infra"), 6, _(56, 59));
301     strnfmt(buf, sizeof(buf), _("%2dft", "%2dft"), 10 * rp_ptr->infra);
302     c_put_str(TERM_L_BLUE, buf, 6, _(67, 65));
303
304     clear_from(10);
305     screen_save();
306     do_cmd_options_aux(player_ptr, OPT_PAGE_BIRTH, _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
307     screen_load();
308 }
309
310 static void display_auto_roller_success_rate(const int col)
311 {
312     if (!autoroller) {
313         return;
314     }
315
316     put_str(_("最小値", " Limit"), 2, col + 13);
317     put_str(_("現在値", "  Roll"), 2, col + 24);
318
319     char buf[32];
320
321     if (autoroll_chance >= 1) {
322         strnfmt(buf, sizeof(buf), _("確率 :  1/%8d00", "Prob :  1/%8d00"), autoroll_chance);
323     } else if (autoroll_chance == -999) {
324         angband_strcpy(buf, _("確率 :     不可能", "Prob :     Impossible"), sizeof(buf));
325     } else {
326         angband_strcpy(buf, _("確率 :     1/10000以上", "Prob :     >1/10000"), sizeof(buf));
327     }
328     put_str(buf, 11, col + 10);
329
330     put_str(_("注意 : 体格等のオートローラを併用時は、上記確率より困難です。", "Note : Prob may be lower when you use the 'autochara' option."), 22, 5);
331
332     for (int i = 0; i < A_MAX; i++) {
333         put_str(stat_names[i], 3 + i, col + 8);
334         int j = rp_ptr->r_adj[i] + cp_ptr->c_adj[i] + ap_ptr->a_adj[i];
335         int m = adjust_stat(stat_limit[i], j);
336         c_put_str(TERM_L_BLUE, cnv_stat(m), 3 + i, col + 13);
337     }
338 }
339
340 static void auto_roller_count(void)
341 {
342     if (auto_round < 1000000000L) {
343         return;
344     }
345
346     auto_round = 1;
347     if (!autoroller) {
348         return;
349     }
350
351     auto_upper_round++;
352 }
353
354 static bool decide_initial_stat(PlayerType *player_ptr)
355 {
356     if (!autoroller) {
357         return true;
358     }
359
360     bool accept = true;
361     for (int i = 0; i < A_MAX; i++) {
362         if (player_ptr->stat_max[i] < stat_limit[i]) {
363             accept = false;
364             break;
365         }
366     }
367
368     return accept;
369 }
370
371 static bool decide_body_spec(PlayerType *player_ptr, chara_limit_type chara_limit, bool *accept)
372 {
373     if (!*accept) {
374         return false;
375     }
376
377     get_ahw(player_ptr);
378     get_history(player_ptr);
379
380     if (autochara) {
381         if ((player_ptr->age < chara_limit.agemin) || (player_ptr->age > chara_limit.agemax)) {
382             *accept = false;
383         }
384         const auto ht = _(inch_to_cm(player_ptr->ht), player_ptr->ht);
385         if ((ht < chara_limit.htmin) || (ht > chara_limit.htmax)) {
386             *accept = false;
387         }
388         const auto wt = _(lb_to_kg(player_ptr->wt), player_ptr->wt);
389         if ((wt < chara_limit.wtmin) || (wt > chara_limit.wtmax)) {
390             *accept = false;
391         }
392         if ((player_ptr->sc < chara_limit.scmin) || (player_ptr->sc > chara_limit.scmax)) {
393             *accept = false;
394         }
395     }
396
397     return *accept;
398 }
399
400 static bool display_auto_roller_count(PlayerType *player_ptr, const int col)
401 {
402     if ((auto_round % AUTOROLLER_STEP) != 0) {
403         return false;
404     }
405
406     birth_put_stats(player_ptr);
407     if (auto_upper_round) {
408         put_str(format("%d%09d", auto_upper_round, auto_round), 10, col + 20);
409     } else {
410         put_str(format("%10d", auto_round), 10, col + 20);
411     }
412     term_fresh();
413     inkey_scan = true;
414     if (inkey()) {
415         get_ahw(player_ptr);
416         get_history(player_ptr);
417         return true;
418     }
419
420     return false;
421 }
422
423 static void exe_auto_roller(PlayerType *player_ptr, chara_limit_type chara_limit, const int col)
424 {
425     while (autoroller || autochara) {
426         get_stats(player_ptr);
427         auto_round++;
428         auto_roller_count();
429         bool accept = decide_initial_stat(player_ptr);
430         if (decide_body_spec(player_ptr, chara_limit, &accept)) {
431             return;
432         }
433
434         if (display_auto_roller_count(player_ptr, col)) {
435             return;
436         }
437     }
438 }
439
440 static bool display_auto_roller_result(PlayerType *player_ptr, bool prev, char *c)
441 {
442     BIT_FLAGS mode = 0;
443     auto &rfu = RedrawingFlagsUpdater::get_instance();
444     static constexpr auto flags = {
445         StatusRecalculatingFlag::BONUS,
446         StatusRecalculatingFlag::HP,
447     };
448     while (true) {
449         rfu.set_flags(flags);
450         update_creature(player_ptr);
451         player_ptr->chp = player_ptr->mhp;
452         player_ptr->csp = player_ptr->msp;
453         (void)display_player(player_ptr, mode);
454         term_gotoxy(2, 23);
455         const char b1 = '[';
456         term_addch(TERM_WHITE, b1);
457         term_addstr(-1, TERM_WHITE, _("'r' 次の数値", "'r'eroll"));
458         if (prev) {
459             term_addstr(-1, TERM_WHITE, _(", 'p' 前の数値", ", 'p'revious"));
460         }
461
462         if (mode) {
463             term_addstr(-1, TERM_WHITE, _(", 'h' その他の情報", ", 'h' Misc."));
464         } else {
465             term_addstr(-1, TERM_WHITE, _(", 'h' 生い立ちを表示", ", 'h'istory"));
466         }
467
468         term_addstr(-1, TERM_WHITE, _(", Enter この数値に決定", ", or Enter to accept"));
469         const char b2 = ']';
470         term_addch(TERM_WHITE, b2);
471         *c = inkey();
472         if (*c == 'Q') {
473             birth_quit();
474         }
475
476         if (*c == 'S') {
477             return false;
478         }
479
480         if (*c == '\r' || *c == '\n' || *c == ESCAPE) {
481             break;
482         }
483
484         if ((*c == ' ') || (*c == 'r')) {
485             break;
486         }
487
488         if (prev && (*c == 'p')) {
489             load_prev_data(player_ptr, true);
490             continue;
491         }
492
493         if ((*c == 'H') || (*c == 'h')) {
494             mode = ((mode != 0) ? 0 : 1);
495             continue;
496         }
497
498         birth_help_option(player_ptr, *c, BirthKind::AUTO_ROLLER);
499         bell();
500     }
501
502     return true;
503 }
504
505 /*
506  * @brief オートロールを回して結果を表示し、その数値に決めるかさらに回すか確認する。
507  * @param player_ptr プレイヤーへの参照ポインタ
508  * @param chara_limit 社会的地位の要求水準
509  * @details 2つめの結果以降は、'p'キーで1つ前のロール結果に戻せる。
510  */
511 static bool display_auto_roller(PlayerType *player_ptr, chara_limit_type chara_limit)
512 {
513     bool prev = false;
514
515     while (true) {
516         int col = 22;
517         if (autoroller || autochara) {
518             term_clear();
519             put_str(_("回数 :", "Round:"), 10, col + 10);
520             put_str(_("(ESCで停止)", "(Hit ESC to stop)"), 13, col + 13);
521         } else {
522             get_stats(player_ptr);
523             get_ahw(player_ptr);
524             get_history(player_ptr);
525         }
526
527         display_auto_roller_success_rate(col);
528         exe_auto_roller(player_ptr, chara_limit, col);
529         if (autoroller || autochara) {
530             sound(SOUND_LEVEL);
531         }
532
533         flush();
534
535         get_extra(player_ptr, true);
536         get_money(player_ptr);
537         player_ptr->chaos_patron = (int16_t)randint0(MAX_PATRON);
538
539         char c;
540         if (!display_auto_roller_result(player_ptr, prev, &c)) {
541             return false;
542         }
543
544         if (c == '\r' || c == '\n' || c == ESCAPE) {
545             break;
546         }
547
548         save_prev_data(player_ptr, &previous_char);
549         previous_char.quick_ok = false;
550         prev = true;
551     }
552
553     return true;
554 }
555
556 /*!
557  * @brief 名前と生い立ちを設定する
558  * @param player_ptr プレイヤーへの参照ポインタ
559  * @details ついでにステータス限界もここで決めている
560  */
561 static void set_name_history(PlayerType *player_ptr)
562 {
563     clear_from(23);
564     get_name(player_ptr);
565     process_player_name(player_ptr, w_ptr->creating_savefile);
566     edit_history(player_ptr);
567     get_max_stats(player_ptr);
568     initialize_virtues(player_ptr);
569     prt(_("[ 'Q' 中断, 'S' 初めから, Enter ゲーム開始 ]", "['Q'uit, 'S'tart over, or Enter to continue]"), 23, _(14, 10));
570 }
571
572 /*!
573  * @brief プレイヤーキャラ作成ウィザード
574  * @details
575  * The delay may be reduced, but is recommended to keep players
576  * from continuously rolling up characters, which can be VERY
577  * expensive CPU wise.  And it cuts down on player stupidity.
578  */
579 bool player_birth_wizard(PlayerType *player_ptr)
580 {
581     display_initial_birth_message(player_ptr);
582     for (int n = 0; n < MAX_SEXES; n++) {
583         put_str(birth_sex_label(n), 12 + (n / 5), 2 + 15 * (n % 5));
584     }
585
586     if (!let_player_build_character(player_ptr)) {
587         return false;
588     }
589
590     display_initial_options(player_ptr);
591     if (autoroller || autochara) {
592         auto_round = 0L;
593         auto_upper_round = 0L;
594         autoroll_chance = 0L;
595     }
596
597     if (autoroller) {
598         if (!get_stat_limits(player_ptr)) {
599             return false;
600         }
601     }
602
603     chara_limit_type chara_limit;
604     initialize_chara_limit(&chara_limit);
605     if (autochara) {
606         if (!get_chara_limits(player_ptr, &chara_limit)) {
607             return false;
608         }
609     }
610
611     clear_from(10);
612     init_turn(player_ptr);
613     if (!display_auto_roller(player_ptr, chara_limit)) {
614         return false;
615     }
616
617     set_name_history(player_ptr);
618     char c = inkey();
619     if (c == 'Q') {
620         birth_quit();
621     }
622
623     if (c == 'S') {
624         return false;
625     }
626
627     init_dungeon_quests(player_ptr);
628     save_prev_data(player_ptr, &previous_char);
629     previous_char.quick_ok = true;
630     return true;
631 }