OSDN Git Service

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