OSDN Git Service

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