OSDN Git Service

Merge branch 'master' of https://github.com/hengband/hengband
[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 "main/sound-definitions-table.h"
22 #include "main/sound-of-music.h"
23 #include "player-base/player-class.h"
24 #include "player-info/class-info.h"
25 #include "player-info/race-info.h"
26 #include "player/patron.h"
27 #include "player/player-personality.h"
28 #include "player/player-sex.h"
29 #include "player/player-status-table.h"
30 #include "player/player-status.h"
31 #include "player/process-name.h"
32 #include "system/game-option-types.h"
33 #include "system/player-type-definition.h"
34 #include "system/redrawing-flags-updater.h"
35 #include "term/screen-processor.h"
36 #include "term/term-color-types.h"
37 #include "term/z-form.h"
38 #include "util/enum-converter.h"
39 #include "util/int-char-converter.h"
40 #include "util/string-processor.h"
41 #include "view/display-birth.h" // 暫定。後で消す予定。
42 #include "view/display-player.h" // 暫定。後で消す.
43 #include "view/display-util.h"
44 #include "world/world.h"
45 #include <sstream>
46
47 /*!
48  * オートローラーの内容を描画する間隔 /
49  * How often the autoroller will update the display and pause
50  * to check for user interuptions.
51  * Bigger values will make the autoroller faster, but slower
52  * system may have problems because the user can't stop the
53  * autoroller for this number of rolls.
54  */
55 #define AUTOROLLER_STEP 54321L
56
57 static void display_initial_birth_message(PlayerType *player_ptr)
58 {
59     term_clear();
60     put_str(_("名前  :", "Name  :"), 1, 26);
61     put_str(_("性別        :", "Sex         :"), 3, 1);
62     put_str(_("種族        :", "Race        :"), 4, 1);
63     put_str(_("職業        :", "Class       :"), 5, 1);
64     c_put_str(TERM_L_BLUE, player_ptr->name, 1, 34);
65     put_str(_("キャラクターを作成します。('S'やり直す, 'Q'終了, '?'ヘルプ)", "Make your character. ('S' Restart, 'Q' Quit, '?' Help)"), 8, 10);
66     put_str(_("注意:《性別》の違いはゲーム上ほとんど影響を及ぼしません。", "Note: Your 'sex' does not have any significant gameplay effects."), 23, 5);
67 }
68
69 /*!
70  * @prief 性別選択画面でヘルプを表示させる
71  * @param player_ptr プレイヤーへの参照ポインタ
72  * @param c 入力したコマンド
73  * @details 他の関数名と被りそうだったので少し眺め
74  */
75 static void display_help_on_sex_select(PlayerType *player_ptr, char c)
76 {
77     if (c == '?') {
78         do_cmd_help(player_ptr);
79     } else if (c == '=') {
80         screen_save();
81         do_cmd_options_aux(player_ptr, OPT_PAGE_BIRTH, _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
82         screen_load();
83     } else if (c != '4' && c != '6') {
84         bell();
85     }
86 }
87
88 /*!
89  * @brief 表示されている性別のラベルを取得する
90  * @param cs 性別の指標
91  * @return std::string 表示するラベルを保持
92  */
93 static std::string birth_sex_label(int cs)
94 {
95     const char p2 = ')';
96     std::stringstream ss;
97
98     if (cs < 0 || cs >= MAX_SEXES) {
99         ss << '*' << p2 << _("ランダム", "Random");
100     } else {
101         ss << I2A(cs) << p2 << sex_info[cs].title;
102     }
103     return ss.str();
104 }
105
106 /*!
107  * @brief プレイヤーの性別選択を行う / Player sex
108  * @param player_ptr プレイヤーへの参照ポインタ
109  * @return やり直すならFALSE、それ以外はTRUE
110  */
111 static bool get_player_sex(PlayerType *player_ptr)
112 {
113     int k = -1;
114     int cs = 0;
115     int os = MAX_SEXES;
116     auto cur = birth_sex_label(os);
117     while (true) {
118         if (cs != os) {
119             put_str(cur, 12 + (os / 5), 2 + 15 * (os % 5));
120             cur = birth_sex_label(cs);
121             c_put_str(TERM_YELLOW, cur, 12 + (cs / 5), 2 + 15 * (cs % 5));
122             os = cs;
123         }
124
125         if (k >= 0) {
126             break;
127         }
128
129         char buf[80];
130         strnfmt(buf, sizeof(buf), _("性別を選んで下さい (%c-%c) ('='初期オプション設定): ", "Choose a sex (%c-%c) ('=' for options): "), I2A(0), I2A(1));
131         put_str(buf, 10, 10);
132         char c = inkey();
133         if (c == 'Q') {
134             birth_quit();
135         }
136
137         if (c == 'S') {
138             return false;
139         }
140
141         if (c == ' ' || c == '\r' || c == '\n') {
142             k = cs == MAX_SEXES ? randint0(MAX_SEXES) : cs;
143             break;
144         }
145
146         if (c == '*') {
147             k = randint0(MAX_SEXES);
148             break;
149         }
150
151         if (c == '4') {
152             if (cs > 0) {
153                 cs--;
154             }
155         }
156
157         if (c == '6') {
158             if (cs < MAX_SEXES) {
159                 cs++;
160             }
161         }
162
163         k = (islower(c) ? A2I(c) : -1);
164         if ((k >= 0) && (k < MAX_SEXES)) {
165             cs = k;
166             continue;
167         } else {
168             k = -1;
169         }
170
171         display_help_on_sex_select(player_ptr, c);
172     }
173
174     player_ptr->psex = i2enum<player_sex>(k);
175     sp_ptr = &sex_info[player_ptr->psex];
176     c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 15);
177     return true;
178 }
179
180 static bool let_player_select_race(PlayerType *player_ptr)
181 {
182     clear_from(10);
183     player_ptr->prace = PlayerRaceType::HUMAN;
184     while (true) {
185         if (!get_player_race(player_ptr)) {
186             return false;
187         }
188
189         clear_from(10);
190         display_wrap_around(race_explanations[enum2i(player_ptr->prace)], 74, 12, 3);
191         if (get_check_strict(player_ptr, _("よろしいですか?", "Are you sure? "), CHECK_DEFAULT_Y)) {
192             break;
193         }
194
195         clear_from(10);
196         c_put_str(TERM_WHITE, "              ", 4, 15);
197     }
198
199     return true;
200 }
201
202 static bool let_player_select_class(PlayerType *player_ptr)
203 {
204     clear_from(10);
205     player_ptr->pclass = PlayerClassType::WARRIOR;
206     while (true) {
207         if (!get_player_class(player_ptr)) {
208             return false;
209         }
210
211         clear_from(10);
212         display_wrap_around(class_explanations[enum2i(player_ptr->pclass)], 74, 12, 3);
213
214         if (get_check_strict(player_ptr, _("よろしいですか?", "Are you sure? "), CHECK_DEFAULT_Y)) {
215             break;
216         }
217
218         c_put_str(TERM_WHITE, "              ", 5, 15);
219     }
220
221     return true;
222 }
223
224 static bool let_player_select_personality(PlayerType *player_ptr)
225 {
226     player_ptr->ppersonality = PERSONALITY_ORDINARY;
227     while (true) {
228         if (!get_player_personality(player_ptr)) {
229             return false;
230         }
231
232         clear_from(10);
233         display_wrap_around(personality_explanations[player_ptr->ppersonality], 74, 12, 3);
234
235         if (get_check_strict(player_ptr, _("よろしいですか?", "Are you sure? "), CHECK_DEFAULT_Y)) {
236             break;
237         }
238
239         c_put_str(TERM_L_BLUE, player_ptr->name, 1, 34);
240         prt("", 1, 34 + strlen(player_ptr->name));
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         if ((player_ptr->ht < chara_limit.htmin) || (player_ptr->ht > chara_limit.htmax)) {
385             *accept = false;
386         }
387         if ((player_ptr->wt < chara_limit.wtmin) || (player_ptr->wt > chara_limit.wtmax)) {
388             *accept = false;
389         }
390         if ((player_ptr->sc < chara_limit.scmin) || (player_ptr->sc > chara_limit.scmax)) {
391             *accept = false;
392         }
393     }
394
395     return *accept;
396 }
397
398 static bool display_auto_roller_count(PlayerType *player_ptr, const int col)
399 {
400     if ((auto_round % AUTOROLLER_STEP) != 0) {
401         return false;
402     }
403
404     birth_put_stats(player_ptr);
405     if (auto_upper_round) {
406         put_str(format("%d%09d", auto_upper_round, auto_round), 10, col + 20);
407     } else {
408         put_str(format("%10d", auto_round), 10, col + 20);
409     }
410     term_fresh();
411     inkey_scan = true;
412     if (inkey()) {
413         get_ahw(player_ptr);
414         get_history(player_ptr);
415         return true;
416     }
417
418     return false;
419 }
420
421 static void exe_auto_roller(PlayerType *player_ptr, chara_limit_type chara_limit, const int col)
422 {
423     while (autoroller || autochara) {
424         get_stats(player_ptr);
425         auto_round++;
426         auto_roller_count();
427         bool accept = decide_initial_stat(player_ptr);
428         if (decide_body_spec(player_ptr, chara_limit, &accept)) {
429             return;
430         }
431
432         if (display_auto_roller_count(player_ptr, col)) {
433             return;
434         }
435     }
436 }
437
438 static bool display_auto_roller_result(PlayerType *player_ptr, bool prev, char *c)
439 {
440     BIT_FLAGS mode = 0;
441     auto &rfu = RedrawingFlagsUpdater::get_instance();
442     static constexpr auto flags = {
443         StatusRecalculatingFlag::BONUS,
444         StatusRecalculatingFlag::HP,
445     };
446     while (true) {
447         rfu.set_flags(flags);
448         update_creature(player_ptr);
449         player_ptr->chp = player_ptr->mhp;
450         player_ptr->csp = player_ptr->msp;
451         (void)display_player(player_ptr, mode);
452         term_gotoxy(2, 23);
453         const char b1 = '[';
454         term_addch(TERM_WHITE, b1);
455         term_addstr(-1, TERM_WHITE, _("'r' 次の数値", "'r'eroll"));
456         if (prev) {
457             term_addstr(-1, TERM_WHITE, _(", 'p' 前の数値", ", 'p'revious"));
458         }
459
460         if (mode) {
461             term_addstr(-1, TERM_WHITE, _(", 'h' その他の情報", ", 'h' Misc."));
462         } else {
463             term_addstr(-1, TERM_WHITE, _(", 'h' 生い立ちを表示", ", 'h'istory"));
464         }
465
466         term_addstr(-1, TERM_WHITE, _(", Enter この数値に決定", ", or Enter to accept"));
467         const char b2 = ']';
468         term_addch(TERM_WHITE, b2);
469         *c = inkey();
470         if (*c == 'Q') {
471             birth_quit();
472         }
473
474         if (*c == 'S') {
475             return false;
476         }
477
478         if (*c == '\r' || *c == '\n' || *c == ESCAPE) {
479             break;
480         }
481
482         if ((*c == ' ') || (*c == 'r')) {
483             break;
484         }
485
486         if (prev && (*c == 'p')) {
487             load_prev_data(player_ptr, true);
488             continue;
489         }
490
491         if ((*c == 'H') || (*c == 'h')) {
492             mode = ((mode != 0) ? 0 : 1);
493             continue;
494         }
495
496         birth_help_option(player_ptr, *c, BirthKind::AUTO_ROLLER);
497         bell();
498     }
499
500     return true;
501 }
502
503 /*
504  * @brief オートロールを回して結果を表示し、その数値に決めるかさらに回すか確認する。
505  * @param player_ptr プレイヤーへの参照ポインタ
506  * @param chara_limit 社会的地位の要求水準
507  * @details 2つめの結果以降は、'p'キーで1つ前のロール結果に戻せる。
508  */
509 static bool display_auto_roller(PlayerType *player_ptr, chara_limit_type chara_limit)
510 {
511     bool prev = false;
512
513     while (true) {
514         int col = 22;
515         if (autoroller || autochara) {
516             term_clear();
517             put_str(_("回数 :", "Round:"), 10, col + 10);
518             put_str(_("(ESCで停止)", "(Hit ESC to stop)"), 13, col + 13);
519         } else {
520             get_stats(player_ptr);
521             get_ahw(player_ptr);
522             get_history(player_ptr);
523         }
524
525         display_auto_roller_success_rate(col);
526         exe_auto_roller(player_ptr, chara_limit, col);
527         if (autoroller || autochara) {
528             sound(SOUND_LEVEL);
529         }
530
531         flush();
532
533         get_extra(player_ptr, true);
534         get_money(player_ptr);
535         player_ptr->chaos_patron = (int16_t)randint0(MAX_PATRON);
536
537         char c;
538         if (!display_auto_roller_result(player_ptr, prev, &c)) {
539             return false;
540         }
541
542         if (c == '\r' || c == '\n' || c == ESCAPE) {
543             break;
544         }
545
546         save_prev_data(player_ptr, &previous_char);
547         previous_char.quick_ok = false;
548         prev = true;
549     }
550
551     return true;
552 }
553
554 /*!
555  * @brief 名前と生い立ちを設定する
556  * @param player_ptr プレイヤーへの参照ポインタ
557  * @details ついでにステータス限界もここで決めている
558  */
559 static void set_name_history(PlayerType *player_ptr)
560 {
561     clear_from(23);
562     get_name(player_ptr);
563     process_player_name(player_ptr, w_ptr->creating_savefile);
564     edit_history(player_ptr);
565     get_max_stats(player_ptr);
566     initialize_virtues(player_ptr);
567     prt(_("[ 'Q' 中断, 'S' 初めから, Enter ゲーム開始 ]", "['Q'uit, 'S'tart over, or Enter to continue]"), 23, _(14, 10));
568 }
569
570 /*!
571  * @brief プレイヤーキャラ作成ウィザード
572  * @details
573  * The delay may be reduced, but is recommended to keep players
574  * from continuously rolling up characters, which can be VERY
575  * expensive CPU wise.  And it cuts down on player stupidity.
576  */
577 bool player_birth_wizard(PlayerType *player_ptr)
578 {
579     display_initial_birth_message(player_ptr);
580     for (int n = 0; n < MAX_SEXES; n++) {
581         put_str(birth_sex_label(n), 12 + (n / 5), 2 + 15 * (n % 5));
582     }
583
584     if (!let_player_build_character(player_ptr)) {
585         return false;
586     }
587
588     display_initial_options(player_ptr);
589     if (autoroller || autochara) {
590         auto_round = 0L;
591         auto_upper_round = 0L;
592         autoroll_chance = 0L;
593     }
594
595     if (autoroller) {
596         if (!get_stat_limits(player_ptr)) {
597             return false;
598         }
599     }
600
601     chara_limit_type chara_limit;
602     initialize_chara_limit(&chara_limit);
603     if (autochara) {
604         if (!get_chara_limits(player_ptr, &chara_limit)) {
605             return false;
606         }
607     }
608
609     clear_from(10);
610     init_turn(player_ptr);
611     if (!display_auto_roller(player_ptr, chara_limit)) {
612         return false;
613     }
614
615     set_name_history(player_ptr);
616     char c = inkey();
617     if (c == 'Q') {
618         birth_quit();
619     }
620
621     if (c == 'S') {
622         return false;
623     }
624
625     init_dungeon_quests(player_ptr);
626     save_prev_data(player_ptr, &previous_char);
627     previous_char.quick_ok = true;
628     return true;
629 }