OSDN Git Service

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