Angband 3.0.9b.
[angband.git] / src / birth.c
blob10cffcae59ba79bb66521862a2affa1431bee0f2
1 /* File: birth.c */
3 /*
4 * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6 * This software may be copied and distributed for educational, research,
7 * and not for profit purposes provided that this copyright and statement
8 * are included in all such copies. Other copyrights may also apply.
9 */
11 #include "angband.h"
12 #include "cmds.h"
13 #include "script.h"
18 * Forward declare
20 typedef struct birther /*lovely*/ birther; /*sometimes we think she's a dream*/
23 * A structure to hold "rolled" information, and any
24 * other useful state for the birth process.
26 struct birther
28 s16b age;
29 s16b wt;
30 s16b ht;
31 s16b sc;
33 s32b au;
35 s16b stat[A_MAX];
37 char *name;
39 char history[250];
45 * The last character displayed
47 static birther prev;
51 * Current stats (when rolling a character).
53 static s16b stat_use[A_MAX];
58 * Save the currently rolled data for later.
60 static void save_prev_data(void)
62 int i;
65 /*** Save the current data ***/
67 /* Save the data */
68 prev.age = p_ptr->age;
69 prev.wt = p_ptr->wt;
70 prev.ht = p_ptr->ht;
71 prev.sc = p_ptr->sc;
72 prev.au = p_ptr->au;
74 /* Save the stats */
75 for (i = 0; i < A_MAX; i++)
77 prev.stat[i] = p_ptr->stat_max[i];
80 /* Save the history */
81 my_strcpy(prev.history, p_ptr->history, sizeof(prev.history));
86 * Load the previously rolled data.
88 static void load_prev_data(void)
90 int i;
92 birther temp;
95 /*** Save the current data ***/
97 /* Save the data */
98 temp.age = p_ptr->age;
99 temp.wt = p_ptr->wt;
100 temp.ht = p_ptr->ht;
101 temp.sc = p_ptr->sc;
102 temp.au = p_ptr->au;
104 /* Save the stats */
105 for (i = 0; i < A_MAX; i++)
107 temp.stat[i] = p_ptr->stat_max[i];
110 /* Save the history */
111 my_strcpy(temp.history, p_ptr->history, sizeof(temp.history));
114 /*** Load the previous data ***/
116 /* Load the data */
117 p_ptr->age = prev.age;
118 p_ptr->wt = prev.wt;
119 p_ptr->ht = prev.ht;
120 p_ptr->sc = prev.sc;
121 p_ptr->au = prev.au;
123 /* Load the stats */
124 for (i = 0; i < A_MAX; i++)
126 p_ptr->stat_max[i] = prev.stat[i];
127 p_ptr->stat_cur[i] = prev.stat[i];
130 /* Load the history */
131 my_strcpy(p_ptr->history, prev.history, sizeof(p_ptr->history));
134 /*** Save the current data ***/
136 /* Save the data */
137 prev.age = temp.age;
138 prev.wt = temp.wt;
139 prev.ht = temp.ht;
140 prev.sc = temp.sc;
141 prev.au = temp.au;
143 /* Save the stats */
144 for (i = 0; i < A_MAX; i++)
146 prev.stat[i] = temp.stat[i];
149 /* Save the history */
150 my_strcpy(prev.history, temp.history, sizeof(prev.history));
157 * Adjust a stat by an amount.
159 * This just uses "modify_stat_value()" unless "maximize" mode is false,
160 * and a positive bonus is being applied, in which case, a special hack
161 * is used, with the "auto_roll" flag affecting the result.
163 * The "auto_roll" flag selects "maximal" changes for use with the
164 * auto-roller initialization code. Otherwise, if "maximize" mode
165 * is being used, the changes are fixed. Otherwise, semi-random
166 * changes will occur, with larger changes at lower values.
168 static int adjust_stat(int value, int amount, int auto_roll)
170 /* Negative amounts or maximize mode */
171 if ((amount < 0) || adult_maximize)
173 return (modify_stat_value(value, amount));
176 /* Special hack */
177 else
179 int i;
181 /* Apply reward */
182 for (i = 0; i < amount; i++)
184 if (value < 18)
186 value++;
188 else if (value < 18+70)
190 value += ((auto_roll ? 15 : randint(15)) + 5);
192 else if (value < 18+90)
194 value += ((auto_roll ? 6 : randint(6)) + 2);
196 else if (value < 18+100)
198 value++;
203 /* Return the result */
204 return (value);
211 * Roll for a characters stats
213 * For efficiency, we include a chunk of "calc_bonuses()".
215 static void get_stats(void)
217 int i, j;
219 int bonus;
221 int dice[18];
224 /* Roll and verify some stats */
225 while (TRUE)
227 /* Roll some dice */
228 for (j = i = 0; i < 18; i++)
230 /* Roll the dice */
231 dice[i] = randint(3 + i % 3);
233 /* Collect the maximum */
234 j += dice[i];
237 /* Verify totals */
238 if ((j > 42) && (j < 54)) break;
241 /* Roll the stats */
242 for (i = 0; i < A_MAX; i++)
244 /* Extract 5 + 1d3 + 1d4 + 1d5 */
245 j = 5 + dice[3*i] + dice[3*i+1] + dice[3*i+2];
247 /* Save that value */
248 p_ptr->stat_max[i] = j;
250 /* Obtain a "bonus" for "race" and "class" */
251 bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i];
253 /* Variable stat maxes */
254 if (adult_maximize)
256 /* Start fully healed */
257 p_ptr->stat_cur[i] = p_ptr->stat_max[i];
259 /* Efficiency -- Apply the racial/class bonuses */
260 stat_use[i] = modify_stat_value(p_ptr->stat_max[i], bonus);
263 /* Fixed stat maxes */
264 else
266 /* Apply the bonus to the stat (somewhat randomly) */
267 stat_use[i] = adjust_stat(p_ptr->stat_max[i], bonus, FALSE);
269 /* Save the resulting stat maximum */
270 p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stat_use[i];
277 * Roll for some info that the auto-roller ignores
279 static void get_extra(void)
281 int i, j, min_value, max_value;
284 /* Level one */
285 p_ptr->max_lev = p_ptr->lev = 1;
287 /* Experience factor */
288 p_ptr->expfact = rp_ptr->r_exp + cp_ptr->c_exp;
290 /* Hitdice */
291 p_ptr->hitdie = rp_ptr->r_mhp + cp_ptr->c_mhp;
293 /* Initial hitpoints */
294 p_ptr->mhp = p_ptr->hitdie;
296 /* Minimum hitpoints at highest level */
297 min_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 3) / 8;
298 min_value += PY_MAX_LEVEL;
300 /* Maximum hitpoints at highest level */
301 max_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 5) / 8;
302 max_value += PY_MAX_LEVEL;
304 /* Pre-calculate level 1 hitdice */
305 p_ptr->player_hp[0] = p_ptr->hitdie;
307 /* Roll out the hitpoints */
308 while (TRUE)
310 /* Roll the hitpoint values */
311 for (i = 1; i < PY_MAX_LEVEL; i++)
313 j = randint(p_ptr->hitdie);
314 p_ptr->player_hp[i] = p_ptr->player_hp[i-1] + j;
317 /* XXX Could also require acceptable "mid-level" hitpoints */
319 /* Require "valid" hitpoints at highest level */
320 if (p_ptr->player_hp[PY_MAX_LEVEL-1] < min_value) continue;
321 if (p_ptr->player_hp[PY_MAX_LEVEL-1] > max_value) continue;
323 /* Acceptable */
324 break;
330 * Get the racial history, and social class, using the "history charts".
332 static void get_history(void)
334 int i, chart, roll, social_class;
337 /* Clear the previous history strings */
338 p_ptr->history[0] = '\0';
341 /* Initial social class */
342 social_class = randint(4);
344 /* Starting place */
345 chart = rp_ptr->hist;
348 /* Process the history */
349 while (chart)
351 /* Start over */
352 i = 0;
354 /* Roll for nobility */
355 roll = randint(100);
357 /* Get the proper entry in the table */
358 while ((chart != h_info[i].chart) || (roll > h_info[i].roll)) i++;
360 /* Get the textual history */
361 my_strcat(p_ptr->history, (h_text + h_info[i].text), sizeof(p_ptr->history));
363 /* Add in the social class */
364 social_class += (int)(h_info[i].bonus) - 50;
366 /* Enter the next chart */
367 chart = h_info[i].next;
372 /* Verify social class */
373 if (social_class > 100) social_class = 100;
374 else if (social_class < 1) social_class = 1;
376 /* Save the social class */
377 p_ptr->sc = social_class;
382 * Computes character's age, height, and weight
384 static void get_ahw(void)
386 /* Calculate the age */
387 p_ptr->age = rp_ptr->b_age + randint(rp_ptr->m_age);
389 /* Calculate the height/weight for males */
390 if (p_ptr->psex == SEX_MALE)
392 p_ptr->ht = Rand_normal(rp_ptr->m_b_ht, rp_ptr->m_m_ht);
393 p_ptr->wt = Rand_normal(rp_ptr->m_b_wt, rp_ptr->m_m_wt);
396 /* Calculate the height/weight for females */
397 else if (p_ptr->psex == SEX_FEMALE)
399 p_ptr->ht = Rand_normal(rp_ptr->f_b_ht, rp_ptr->f_m_ht);
400 p_ptr->wt = Rand_normal(rp_ptr->f_b_wt, rp_ptr->f_m_wt);
408 * Get the player's starting money
410 static void get_money(void)
412 int i;
414 int gold;
416 /* Social Class determines starting gold */
417 gold = (p_ptr->sc * 6) + randint(100) + 300;
419 /* Process the stats */
420 for (i = 0; i < A_MAX; i++)
422 /* Mega-Hack -- reduce gold for high stats */
423 if (stat_use[i] >= 18+50) gold -= 300;
424 else if (stat_use[i] >= 18+20) gold -= 200;
425 else if (stat_use[i] > 18) gold -= 150;
426 else gold -= (stat_use[i] - 8) * 10;
429 /* Minimum 100 gold */
430 if (gold < 100) gold = 100;
432 /* Save the gold */
433 p_ptr->au = gold;
439 * Clear all the global "character" data
441 static void player_wipe(bool really_wipe)
443 int i;
445 byte psex, prace, pclass;
447 if (really_wipe)
449 psex = 0;
450 prace = 0;
451 pclass = 0;
453 else
455 /* Backup the player choices */
456 psex = p_ptr->psex;
457 prace = p_ptr->prace;
458 pclass = p_ptr->pclass;
461 /* Wipe the player */
462 (void)WIPE(p_ptr, player_type);
464 /* Restore the choices */
465 p_ptr->psex = psex;
466 p_ptr->prace = prace;
467 p_ptr->pclass = pclass;
469 /* Clear the inventory */
470 for (i = 0; i < INVEN_TOTAL; i++)
472 object_wipe(&inventory[i]);
476 /* Start with no artifacts made yet */
477 for (i = 0; i < z_info->a_max; i++)
479 artifact_type *a_ptr = &a_info[i];
480 a_ptr->cur_num = 0;
484 /* Start with no quests */
485 for (i = 0; i < MAX_Q_IDX; i++)
487 q_list[i].level = 0;
490 /* Add a special quest */
491 q_list[0].level = 99;
493 /* Add a second quest */
494 q_list[1].level = 100;
497 /* Reset the "objects" */
498 for (i = 1; i < z_info->k_max; i++)
500 object_kind *k_ptr = &k_info[i];
502 /* Reset "tried" */
503 k_ptr->tried = FALSE;
505 /* Reset "aware" */
506 k_ptr->aware = FALSE;
510 /* Reset the "monsters" */
511 for (i = 1; i < z_info->r_max; i++)
513 monster_race *r_ptr = &r_info[i];
514 monster_lore *l_ptr = &l_list[i];
516 /* Hack -- Reset the counter */
517 r_ptr->cur_num = 0;
519 /* Hack -- Reset the max counter */
520 r_ptr->max_num = 100;
522 /* Hack -- Reset the max counter */
523 if (r_ptr->flags1 & (RF1_UNIQUE)) r_ptr->max_num = 1;
525 /* Clear player kills */
526 l_ptr->pkills = 0;
530 /* Hack -- no ghosts */
531 r_info[z_info->r_max-1].max_num = 0;
534 /* Hack -- Well fed player */
535 p_ptr->food = PY_FOOD_FULL - 1;
538 /* None of the spells have been learned yet */
539 for (i = 0; i < PY_MAX_SPELLS; i++) p_ptr->spell_order[i] = 99;
545 * Init players with some belongings
547 * Having an item identifies it and makes the player "aware" of its purpose.
549 static void player_outfit(void)
551 int i;
552 const start_item *e_ptr;
553 object_type *i_ptr;
554 object_type object_type_body;
557 /* Hack -- Give the player his equipment */
558 for (i = 0; i < MAX_START_ITEMS; i++)
560 /* Access the item */
561 e_ptr = &(cp_ptr->start_items[i]);
563 /* Get local object */
564 i_ptr = &object_type_body;
566 /* Hack -- Give the player an object */
567 if (e_ptr->tval > 0)
569 /* Get the object_kind */
570 int k_idx = lookup_kind(e_ptr->tval, e_ptr->sval);
572 /* Valid item? */
573 if (!k_idx) continue;
575 /* Prepare the item */
576 object_prep(i_ptr, k_idx);
577 i_ptr->number = (byte)rand_range(e_ptr->min, e_ptr->max);
579 object_aware(i_ptr);
580 object_known(i_ptr);
581 (void)inven_carry(i_ptr);
582 k_info[k_idx].everseen = TRUE;
587 /* Hack -- give the player hardcoded equipment XXX */
589 /* Get local object */
590 i_ptr = &object_type_body;
592 /* Hack -- Give the player some food */
593 object_prep(i_ptr, lookup_kind(TV_FOOD, SV_FOOD_RATION));
594 i_ptr->number = (byte)rand_range(3, 7);
595 object_aware(i_ptr);
596 object_known(i_ptr);
597 k_info[i_ptr->k_idx].everseen = TRUE;
598 (void)inven_carry(i_ptr);
601 /* Get local object */
602 i_ptr = &object_type_body;
604 /* Hack -- Give the player some torches */
605 object_prep(i_ptr, lookup_kind(TV_LITE, SV_LITE_TORCH));
606 i_ptr->number = (byte)rand_range(3, 7);
607 i_ptr->timeout = rand_range(3, 7) * 500;
608 object_aware(i_ptr);
609 object_known(i_ptr);
610 k_info[i_ptr->k_idx].everseen = TRUE;
611 (void)inven_carry(i_ptr);
615 /* Locations of the tables on the screen */
616 #define HEADER_ROW 1
617 #define QUESTION_ROW 7
618 #define TABLE_ROW 10
620 #define QUESTION_COL 2
621 #define SEX_COL 2
622 #define RACE_COL 14
623 #define RACE_AUX_COL 29
624 #define CLASS_COL 29
625 #define CLASS_AUX_COL 50
628 * Clear the previous question
630 static void clear_question(void)
632 int i;
634 for (i = QUESTION_ROW; i < TABLE_ROW; i++)
636 /* Clear line, position cursor */
637 Term_erase(0, i, 255);
640 /* =================================================== */
642 /* gender/race/classs menu selector */
645 * Display additional information about each race during the selection.
647 static void race_aux_hook(int race, void *db, const region *reg)
649 int i;
650 char s[50];
652 if (race == z_info->p_max) return;
654 /* Display relevant details. */
655 for (i = 0; i < A_MAX; i++)
657 strnfmt(s, sizeof(s), "%s%+d", stat_names_reduced[i],
658 p_info[race].r_adj[i]);
659 Term_putstr(RACE_AUX_COL, TABLE_ROW + i, -1, TERM_WHITE, s);
662 strnfmt(s, sizeof(s), "Hit die: %d ", p_info[race].r_mhp);
663 Term_putstr(RACE_AUX_COL, TABLE_ROW + A_MAX, -1, TERM_WHITE, s);
664 strnfmt(s, sizeof(s), "Experience: %d%% ", p_info[race].r_exp);
665 Term_putstr(RACE_AUX_COL, TABLE_ROW + A_MAX + 1, -1, TERM_WHITE, s);
666 strnfmt(s, sizeof(s), "Infravision: %d ft ", p_info[race].infra * 10);
667 Term_putstr(RACE_AUX_COL, TABLE_ROW + A_MAX + 2, -1, TERM_WHITE, s);
672 * Display additional information about each class during the selection.
674 static void class_aux_hook(int class_idx, void *db, const region *loc)
676 int i;
677 char s[128];
679 if (class_idx == z_info->c_max) return;
681 /* Display relevant details. */
682 for (i = 0; i < A_MAX; i++)
684 strnfmt(s, sizeof(s), "%s%+d", stat_names_reduced[i],
685 c_info[class_idx].c_adj[i]);
686 Term_putstr(CLASS_AUX_COL, TABLE_ROW + i, -1, TERM_WHITE, s);
689 strnfmt(s, sizeof(s), "Hit die: %d ", c_info[class_idx].c_mhp);
690 Term_putstr(CLASS_AUX_COL, TABLE_ROW + A_MAX, -1, TERM_WHITE, s);
691 strnfmt(s, sizeof(s), "Experience: %d%% ", c_info[class_idx].c_exp);
692 Term_putstr(CLASS_AUX_COL, TABLE_ROW + A_MAX + 1, -1, TERM_WHITE, s);
696 static region gender_region = {SEX_COL, TABLE_ROW, 15, -2};
697 static region race_region = {RACE_COL, TABLE_ROW, 15, -2};
698 static region class_region = {CLASS_COL, TABLE_ROW, 19, -2};
699 static region roller_region = {44, TABLE_ROW, 21, -2};
702 /* Event handler implementation */
703 static bool handler_aux(char cmd, int oid, byte *val, int max, int mask, cptr topic)
705 if (cmd == '\xff' || cmd == '\r') {
706 *val = oid;
708 else if (cmd == '*') {
709 for(;;)
711 oid = rand_int(max);
712 *val = oid;
713 if(mask & (1L << oid)) break;
716 else if (cmd == '=')
718 do_cmd_options();
719 return FALSE;
721 else if (cmd == KTRL('X'))
723 quit(NULL);
725 else if (cmd == '?') {
726 char buf[80];
727 strnfmt(buf, sizeof(buf), "%s#%s", "birth.txt", topic);
728 screen_save();
729 show_file(buf, NULL, 0, 0);
730 screen_load();
731 return FALSE;
733 else return FALSE;
735 sp_ptr = &sex_info[p_ptr->psex];
736 rp_ptr = &p_info[p_ptr->prace];
737 cp_ptr = &c_info[p_ptr->pclass];
738 mp_ptr = &cp_ptr->spells;
739 return TRUE;
742 /* GENDER */
743 /* Display a gender */
744 static void display_gender(menu_type *menu, int oid, bool cursor,
745 int row, int col, int width)
747 byte attr = curs_attrs[CURS_KNOWN][0 != cursor];
748 c_put_str(attr, sex_info[oid].title, row, col);
751 static bool gender_handler(char cmd, void *db, int oid)
753 return handler_aux(cmd, oid, &p_ptr->psex, SEX_MALE+1,
754 0xffffffff, sex_info[oid].title);
757 /* RACE */
758 static void display_race(menu_type *menu, int oid, bool cursor,
759 int row, int col, int width)
761 byte attr = curs_attrs[CURS_KNOWN][0 != cursor];
762 c_put_str(attr, p_name + p_info[oid].name, row, col);
765 static bool race_handler(char cmd, void *db, int oid)
767 return handler_aux(cmd, oid, &p_ptr->prace, z_info->p_max,
768 0xffffffff, p_name + p_info[oid].name);
771 /* CLASS */
772 static void display_class(menu_type *menu, int oid, bool cursor,
773 int row, int col, int width)
775 byte attr = curs_attrs[0 != (rp_ptr->choice & (1L << oid))][0 != cursor];
776 c_put_str(attr, c_name + c_info[oid].name, row, col);
779 static bool class_handler(char cmd, void *db, int oid)
781 return handler_aux(cmd, oid, &p_ptr->pclass, z_info->c_max,
782 (rp_ptr->choice), c_name + c_info[oid].name);
785 /* ROLLER */
786 static void display_roller(menu_type *menu, int oid, bool cursor,
787 int row, int col, int width)
789 byte attr = curs_attrs[CURS_KNOWN][0 != cursor];
790 const char *str;
792 if (oid == 0)
793 str = "Point-based";
794 else if (oid == 1)
795 str = "Autoroller";
796 else
797 str = "Standard roller";
799 c_prt(attr, str, row, col);
803 static byte roller_type = 0;
804 #define ROLLER_POINT 0
805 #define ROLLER_AUTO 1
806 #define ROLLER_STD 2
808 static bool roller_handler(char cmd, void *db, int oid)
810 if (cmd == '\xff' || cmd == '\r')
812 roller_type = oid;
813 return TRUE;
815 else if (cmd == '*')
817 roller_type = 2;
818 return TRUE;
820 else if(cmd == '=')
821 do_cmd_options();
822 else if(cmd == KTRL('X'))
823 quit(NULL);
824 else if(cmd == '?') {
825 char buf[80];
826 char *str;
828 if (oid == 0)
829 str = "Point-based";
830 else if (oid == 1)
831 str = "Autoroller";
832 else
833 str = "Standard roller";
835 strnfmt(buf, sizeof(buf), "%s#%s", "birth.txt", str);
836 screen_save();
837 show_file(buf, NULL, 0, 0);
838 screen_load();
841 return FALSE;
845 static const menu_iter menu_defs[] = {
846 { 0, 0, 0, display_gender, gender_handler },
847 { 0, 0, 0, display_race, race_handler },
848 { 0, 0, 0, display_class, class_handler },
849 { 0, 0, 0, display_roller, roller_handler },
852 /* Menu display and selector */
854 #define ASEX 0
855 #define ARACE 1
856 #define ACLASS 2
857 #define AROLL 3
861 static bool choose_character(bool start_at_end)
863 int i = 0;
865 const region *regions[] = { &gender_region, &race_region, &class_region, &roller_region };
866 byte *values[4]; /* { &p_ptr->psex, &p_ptr->prace, &p_ptr->pclass }; */
867 int limits[4]; /* { SEX_MALE +1, z_info->p_max, z_info->c_max }; */
869 menu_type menu;
871 const char *hints[] =
873 "Your 'sex' does not have any significant gameplay effects.",
874 "Your 'race' determines various intrinsic factors and bonuses.",
875 "Your 'class' determines various intrinsic abilities and bonuses",
876 "Your choice of character generation. Point-based is recommended."
879 typedef void (*browse_f) (int oid, void *, const region *loc);
880 browse_f browse[] = {NULL, race_aux_hook, class_aux_hook, NULL };
882 /* Stupid ISO C array initialization. */
883 values[ASEX] = &p_ptr->psex;
884 values[ARACE] = &p_ptr->prace;
885 values[ACLASS] = &p_ptr->pclass;
886 values[AROLL] = &roller_type;
887 limits[ASEX] = SEX_MALE + 1;
888 limits[ARACE] = z_info->p_max;
889 limits[ACLASS] = z_info->c_max;
890 limits[AROLL] = 3;
892 WIPE(&menu, menu);
893 menu.cmd_keys = "?=*\r\n\x18"; /* ?, ,= *, \n, <ctl-X> */
894 menu.selections = lower_case;
896 while (i < (int)N_ELEMENTS(menu_defs))
898 event_type cx;
899 int cursor = *values[i];
901 menu.flags = MN_DBL_TAP;
902 menu.count = limits[i];
903 menu.browse_hook = browse[i];
904 menu_init2(&menu, find_menu_skin(MN_SCROLL), &menu_defs[i], regions[i]);
906 clear_question();
907 Term_putstr(QUESTION_COL, QUESTION_ROW, -1, TERM_YELLOW, hints[i]);
909 if (start_at_end)
911 menu_refresh(&menu);
912 i++;
913 if (i == N_ELEMENTS(menu_defs) - 1)
915 start_at_end = FALSE;
918 else
920 cx = menu_select(&menu, &cursor, 0);
922 if (cx.key == ESCAPE || cx.type == EVT_BACK)
924 if (i > 0)
926 /* Move back one menu */
927 *values[i] = cursor;
928 region_erase(regions[i]);
929 i--;
932 else if (cx.key == '*')
934 /* Force refresh */
935 Term_key_push('6');
936 continue;
939 /* Selection! */
940 else if(cx.key == '\r' || cx.key == '\n' || cx.key == '\xff')
941 i++;
945 return TRUE;
950 * Helper function for 'player_birth()'.
952 * This function allows the player to select a sex, race, and class, and
953 * modify options (including the birth options).
955 static bool player_birth_aux_1(bool start_at_end)
957 int i;
959 /*** Instructions ***/
961 /* Clear screen */
962 Term_clear();
964 /* Output to the screen */
965 text_out_hook = text_out_to_screen;
967 /* Indent output */
968 text_out_indent = QUESTION_COL;
969 Term_gotoxy(QUESTION_COL, HEADER_ROW);
971 /* Display some helpful information */
972 text_out_c(TERM_L_BLUE,
973 "Please select your character from the menu below:\n\n");
974 text_out("Use the ");
975 text_out_c(TERM_L_GREEN, "movement keys");
976 text_out(" to scroll the menu, ");
977 text_out_c(TERM_L_GREEN, "Enter");
978 text_out(" to select the current menu item, '");
979 text_out_c(TERM_L_GREEN, "*");
980 text_out("' for a random menu item, '");
981 text_out_c(TERM_L_GREEN, "ESC");
982 text_out("' to step back through the birth process, '");
983 text_out_c(TERM_L_GREEN, "=");
984 text_out("' for the birth options, '");
985 text_out_c(TERM_L_GREEN, "?");
986 text_out("' for help, or '");
987 text_out_c(TERM_L_GREEN, "Ctrl-X");
988 text_out("' to quit.");
990 /* Reset text_out() indentation */
991 text_out_indent = 0;
993 if (!choose_character(start_at_end)) return FALSE;
996 /* Set adult options from birth options */
997 for (i = OPT_BIRTH; i < OPT_CHEAT; i++)
998 op_ptr->opt[OPT_ADULT + (i - OPT_BIRTH)] = op_ptr->opt[i];
1000 /* Reset score options from cheat options */
1001 for (i = OPT_CHEAT; i < OPT_ADULT; i++)
1002 op_ptr->opt[OPT_SCORE + (i - OPT_CHEAT)] = op_ptr->opt[i];
1004 /* Reset squelch bits */
1005 for (i = 0; i < z_info->k_max; i++)
1006 k_info[i].squelch = FALSE;
1008 /* Clear the squelch bytes */
1009 for (i = 0; i < SQUELCH_BYTES; i++)
1010 squelch_level[i] = 0;
1013 /* Done */
1014 return (TRUE);
1017 /* =================================================== */
1020 * Initial stat costs (initial stats always range from 10 to 18 inclusive).
1022 static const int birth_stat_costs[(18-10)+1] = { 0, 1, 2, 4, 7, 11, 16, 22, 30 };
1026 * Helper function for 'player_birth()'.
1028 * This function handles "point-based" character creation.
1030 * The player selects, for each stat, a value from 10 to 18 (inclusive),
1031 * each costing a certain amount of points (as above), from a pool of 48
1032 * available points, to which race/class modifiers are then applied.
1034 * Each unused point is converted into 100 gold pieces.
1036 static int player_birth_aux_2(bool start_at_end)
1038 int i;
1040 int row = 3;
1041 int col = 42;
1043 int stat = 0;
1045 static int stats[A_MAX];
1047 int cost;
1049 char ch;
1051 char buf[80];
1053 bool first_time = FALSE;
1055 /* Clear */
1056 Term_clear();
1058 if (!start_at_end)
1060 first_time = TRUE;
1062 /* Initialize stats */
1063 for (i = 0; i < A_MAX; i++)
1065 /* Initial stats */
1066 stats[i] = 10;
1070 /* Roll for base hitpoints */
1071 get_extra();
1073 /* Roll for age/height/weight */
1074 get_ahw();
1076 /* Roll for social class */
1077 get_history();
1080 /* Interact */
1081 while (1)
1083 /* Reset cost */
1084 cost = 0;
1086 /* Process stats */
1087 for (i = 0; i < A_MAX; i++)
1089 /* Variable stat maxes */
1090 if (adult_maximize)
1092 /* Reset stats */
1093 p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stats[i];
1097 /* Fixed stat maxes */
1098 else
1100 /* Obtain a "bonus" for "race" and "class" */
1101 int bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i];
1103 /* Apply the racial/class bonuses */
1104 p_ptr->stat_cur[i] = p_ptr->stat_max[i] =
1105 modify_stat_value(stats[i], bonus);
1108 /* Total cost */
1109 cost += birth_stat_costs[stats[i] - 10];
1112 /* Restrict cost */
1113 if (cost > 48)
1115 /* Warning */
1116 bell("Excessive stats!");
1118 /* Reduce stat */
1119 stats[stat]--;
1121 /* Recompute costs */
1122 continue;
1125 /* Gold is inversely proportional to cost */
1126 p_ptr->au = (100 * (48 - cost)) + 100;
1128 /* Calculate the bonuses and hitpoints */
1129 p_ptr->update |= (PU_BONUS | PU_HP);
1131 /* Update stuff */
1132 update_stuff();
1134 /* Fully healed */
1135 p_ptr->chp = p_ptr->mhp;
1137 /* Fully rested */
1138 p_ptr->csp = p_ptr->msp;
1140 /* Display the player */
1141 display_player(0);
1143 /* Display the costs header */
1144 put_str("Cost", row - 1, col + 32);
1146 /* Display the costs */
1147 for (i = 0; i < A_MAX; i++)
1149 /* Display cost */
1150 strnfmt(buf, sizeof(buf), "%4d", birth_stat_costs[stats[i] - 10]);
1151 put_str(buf, row + i, col + 32);
1155 /* Prompt XXX XXX XXX */
1156 strnfmt(buf, sizeof(buf), "Total Cost %2d/48. Use 2/8 to move, 4/6 to modify, 'Enter' to accept.", cost);
1157 prt(buf, 0, 0);
1159 /* Place cursor just after cost of current stat */
1160 Term_gotoxy(col + 36, row + stat);
1162 /* Get key */
1163 ch = inkey();
1165 if (ch == KTRL('X'))
1166 quit(NULL);
1168 /* Go back a step, or back to the start of this step */
1169 if (ch == ESCAPE)
1171 if (first_time)
1172 return -1;
1173 else
1174 return 0;
1177 first_time = FALSE;
1179 /* Done */
1180 if ((ch == '\r') || (ch == '\n')) break;
1182 ch = target_dir(ch);
1184 /* Prev stat */
1185 if (ch == 8)
1187 stat = (stat + A_MAX - 1) % A_MAX;
1190 /* Next stat */
1191 if (ch == 2)
1193 stat = (stat + 1) % A_MAX;
1196 /* Decrease stat */
1197 if ((ch == 4) && (stats[stat] > 10))
1199 stats[stat]--;
1202 /* Increase stat */
1203 if ((ch == 6) && (stats[stat] < 18))
1205 stats[stat]++;
1210 /* Done - advance a step*/
1211 return +1;
1215 bool minstat_keypress(char *buf, size_t buflen, size_t *curs, size_t *len, char keypress, bool firsttime)
1217 if (keypress == KTRL('x'))
1218 quit(NULL);
1220 return askfor_aux_keypress(buf, buflen, curs, len, keypress, firsttime);
1225 * Helper function for 'player_birth()'.
1227 * This function handles "auto-rolling" and "random-rolling".
1229 static int player_birth_aux_3(bool start_at_end, bool autoroll)
1231 int i, j, m, v;
1233 bool flag;
1234 static bool prev = FALSE;
1236 char ch;
1238 char b1 = '[';
1239 char b2 = ']';
1241 char buf[80];
1244 /* We'll keep these for when we step "back" into the autoroller */
1245 static s16b stat_limit[A_MAX];
1247 s32b stat_match[A_MAX];
1249 s32b auto_round = 0L;
1251 s32b last_round;
1254 /* Clear */
1255 Term_clear();
1257 /*** Autoroll ***/
1259 /* Initialize */
1260 if (!start_at_end && autoroll)
1262 int mval[A_MAX];
1264 char inp[80];
1266 prev = FALSE;
1268 /* Extra info */
1269 Term_putstr(5, 10, -1, TERM_WHITE,
1270 "The auto-roller will automatically ignore characters which do");
1271 Term_putstr(5, 11, -1, TERM_WHITE,
1272 "not meet the minimum values for any stats specified below.");
1273 Term_putstr(5, 12, -1, TERM_WHITE,
1274 "Note that stats are not independant, so it is not possible to");
1275 Term_putstr(5, 13, -1, TERM_WHITE,
1276 "get perfect (or even high) values for all your stats.");
1278 /* Prompt for the minimum stats */
1279 put_str("Enter minimum value for: ", 15, 2);
1281 /* Output the maximum stats */
1282 for (i = 0; i < A_MAX; i++)
1284 /* Reset the "success" counter */
1285 stat_match[i] = 0;
1287 /* Race/Class bonus */
1288 j = rp_ptr->r_adj[i] + cp_ptr->c_adj[i];
1290 /* Obtain the "maximal" stat */
1291 m = adjust_stat(17, j, TRUE);
1293 /* Save the maximum */
1294 mval[i] = m;
1296 /* Extract a textual format */
1297 /* cnv_stat(m, inp, sizeof(buf); */
1299 /* Above 18 */
1300 if (m > 18)
1302 strnfmt(inp, sizeof(inp), "(Max of 18/%02d):", (m - 18));
1305 /* From 3 to 18 */
1306 else
1308 strnfmt(inp, sizeof(inp), "(Max of %2d):", m);
1311 /* Prepare a prompt */
1312 strnfmt(buf, sizeof(buf), "%-5s%-20s", stat_names[i], inp);
1314 /* Dump the prompt */
1315 put_str(buf, 16 + i, 5);
1318 /* Input the minimum stats */
1319 for (i = 0; i < A_MAX; i++)
1321 /* Get a minimum stat */
1322 while (TRUE)
1324 char *s;
1326 /* Move the cursor */
1327 put_str("", 16 + i, 30);
1329 /* Default */
1330 inp[0] = '\0';
1332 /* Get a response (or escape) */
1333 if (!askfor_aux(inp, 9, minstat_keypress))
1335 if (i == 0)
1336 /* Back a step */
1337 return -1;
1338 else
1339 /* Repeat this step */
1340 return 0;
1343 /* Hack -- add a fake slash */
1344 my_strcat(inp, "/", sizeof(inp));
1346 /* Hack -- look for the "slash" */
1347 s = strchr(inp, '/');
1349 /* Hack -- Nuke the slash */
1350 *s++ = '\0';
1352 /* Hack -- Extract an input */
1353 v = atoi(inp) + atoi(s);
1355 /* Break on valid input */
1356 if (v <= mval[i]) break;
1359 /* Save the minimum stat */
1360 stat_limit[i] = (v > 0) ? v : 0;
1365 /* Clean up */
1366 if (!start_at_end)
1367 clear_from(10);
1369 /*** Generate ***/
1371 /* Roll */
1372 while (TRUE)
1374 if (!start_at_end)
1376 int col = 42;
1378 /* Feedback */
1379 if (autoroll)
1381 Term_clear();
1383 /* Label */
1384 put_str(" Limit", 2, col+5);
1386 /* Label */
1387 put_str(" Freq", 2, col+13);
1389 /* Label */
1390 put_str(" Roll", 2, col+24);
1392 /* Put the minimal stats */
1393 for (i = 0; i < A_MAX; i++)
1395 /* Label stats */
1396 put_str(stat_names[i], 3+i, col);
1398 /* Put the stat */
1399 cnv_stat(stat_limit[i], buf, sizeof(buf));
1400 c_put_str(TERM_L_BLUE, buf, 3+i, col+5);
1403 /* Note when we started */
1404 last_round = auto_round;
1406 /* Label count */
1407 put_str("Round:", 10, col+13);
1409 /* Indicate the state */
1410 put_str("(Hit ESC to stop)", 12, col+13);
1412 /* Auto-roll */
1413 while (1)
1415 bool accept = TRUE;
1417 /* Get a new character */
1418 get_stats();
1420 /* Advance the round */
1421 auto_round++;
1423 /* Hack -- Prevent overflow */
1424 if (auto_round >= 1000000L) break;
1426 /* Check and count acceptable stats */
1427 for (i = 0; i < A_MAX; i++)
1429 /* This stat is okay */
1430 if (stat_use[i] >= stat_limit[i])
1432 stat_match[i]++;
1435 /* This stat is not okay */
1436 else
1438 accept = FALSE;
1442 /* Break if "happy" */
1443 if (accept) break;
1445 /* Take note every 25 rolls */
1446 flag = (!(auto_round % 25L));
1448 /* Update display occasionally */
1449 if (flag || (auto_round < last_round + 100))
1451 /* Put the stats (and percents) */
1452 for (i = 0; i < A_MAX; i++)
1454 /* Put the stat */
1455 cnv_stat(stat_use[i], buf, sizeof(buf));
1456 c_put_str(TERM_L_GREEN, buf, 3+i, col+24);
1458 /* Put the percent */
1459 if (stat_match[i])
1461 int p = 1000L * stat_match[i] / auto_round;
1462 byte attr = (p < 100) ? TERM_YELLOW : TERM_L_GREEN;
1463 strnfmt(buf, sizeof(buf), "%3d.%d%%", p/10, p%10);
1464 c_put_str(attr, buf, 3+i, col+13);
1467 /* Never happened */
1468 else
1470 c_put_str(TERM_RED, "(NONE)", 3+i, col+13);
1474 /* Dump round */
1475 put_str(format("%10ld", auto_round), 10, col+20);
1477 /* Make sure they see everything */
1478 Term_fresh();
1480 /* Do not wait for a key */
1481 inkey_scan = TRUE;
1483 /* Check for a keypress */
1484 if (inkey()) break;
1489 /* Otherwise just get a character */
1490 else
1492 /* Get a new character */
1493 get_stats();
1496 /* Flush input */
1497 flush();
1500 /*** Display ***/
1502 /* Roll for base hitpoints */
1503 get_extra();
1505 /* Roll for age/height/weight */
1506 get_ahw();
1508 /* Roll for social class */
1509 get_history();
1511 /* Roll for gold */
1512 get_money();
1515 start_at_end = FALSE;
1517 /* Input loop */
1518 while (TRUE)
1520 /* Calculate the bonuses and hitpoints */
1521 p_ptr->update |= (PU_BONUS | PU_HP);
1523 /* Update stuff */
1524 update_stuff();
1526 /* Fully healed */
1527 p_ptr->chp = p_ptr->mhp;
1529 /* Fully rested */
1530 p_ptr->csp = p_ptr->msp;
1532 /* Display the player */
1533 display_player(0);
1535 /* Prepare a prompt (must squeeze everything in) */
1536 Term_gotoxy(2, 23);
1537 Term_addch(TERM_WHITE, b1);
1538 Term_addstr(-1, TERM_WHITE, "'r' to reroll");
1539 if (prev) Term_addstr(-1, TERM_WHITE, ", 'p' for prev");
1540 Term_addstr(-1, TERM_WHITE, ", or 'Enter' to accept");
1541 Term_addch(TERM_WHITE, b2);
1543 /* Prompt and get a command */
1544 ch = inkey();
1546 /* Go back to the start of the step, or the previous step */
1547 /* if we're not autorolling. */
1548 if (ch == ESCAPE)
1550 if (autoroll)
1551 return 0;
1552 else
1553 return -1;
1556 /* 'Enter' accepts the roll */
1557 if ((ch == '\r') || (ch == '\n')) break;
1559 /* Reroll this character */
1560 if ((ch == ' ') || (ch == 'r')) break;
1562 /* Previous character */
1563 if (prev && (ch == 'p'))
1565 load_prev_data();
1566 continue;
1569 /* Help */
1570 if (ch == '?')
1572 do_cmd_help();
1573 continue;
1576 if (ch == KTRL('X'))
1577 quit(NULL);
1579 /* Warning */
1580 bell("Illegal auto-roller command!");
1583 /* Are we done? */
1584 if ((ch == '\r') || (ch == '\n')) break;
1586 /* Save this for the "previous" character */
1587 save_prev_data();
1589 /* Note that a previous roll exists */
1590 prev = TRUE;
1593 /* Clear prompt */
1594 clear_from(23);
1596 /* Done - move on a stage */
1597 return +1;
1600 typedef enum
1602 BIRTH_RESTART = 0,
1603 BIRTH_QUESTIONS,
1604 BIRTH_STATS,
1605 BIRTH_NAME,
1606 BIRTH_FINAL_APPROVAL,
1607 BIRTH_ACCEPTED
1608 } birth_stages;
1612 * Helper function for 'player_birth()'.
1614 * See "display_player" for screen layout code.
1616 static void player_birth_aux(void)
1618 char ch;
1619 cptr prompt = "['ESC' to step back, 'S' to start over, or any other key to continue]";
1620 birth_stages state = BIRTH_QUESTIONS;
1621 birth_stages last_state = BIRTH_RESTART;
1623 while (1)
1625 bool start_at_end = (last_state > state);
1626 last_state = state;
1628 switch (state)
1630 case BIRTH_RESTART:
1632 state++;
1633 break;
1636 case BIRTH_QUESTIONS:
1638 /* Race, class, etc. choices */
1639 if (player_birth_aux_1(start_at_end))
1640 state++;
1641 break;
1644 case BIRTH_STATS:
1646 if (roller_type == ROLLER_POINT)
1648 /* Fill stats using point-based methods */
1649 state += player_birth_aux_2(start_at_end);
1651 else
1653 /* Fills stats using the standard- or auto-roller */
1654 state += player_birth_aux_3(start_at_end, roller_type == ROLLER_AUTO);
1656 break;
1659 case BIRTH_NAME:
1661 /* Get a name, prepare savefile */
1662 if (get_name(FALSE))
1663 state++;
1664 else
1665 state--;
1667 break;
1670 case BIRTH_FINAL_APPROVAL:
1672 /* Display the player */
1673 display_player(0);
1675 /* Prompt for it */
1676 prt(prompt, Term->hgt - 1, Term->wid / 2 - strlen(prompt) / 2);
1678 /* Get a key */
1679 ch = inkey();
1681 /* Start over */
1682 if (ch == 'S')
1683 state = BIRTH_RESTART;
1685 if(ch == KTRL('X'))
1686 quit(NULL);
1688 if (ch == ESCAPE)
1689 state--;
1690 else
1691 state++;
1693 /* Clear prompt */
1694 clear_from(23);
1696 break;
1699 case BIRTH_ACCEPTED:
1701 return;
1710 * Create a new character.
1712 * Note that we may be called with "junk" leftover in the various
1713 * fields, so we must be sure to clear them first.
1715 void player_birth(void)
1717 /* Wipe the player properly */
1718 player_wipe(TRUE);
1720 /* Create a new character */
1721 player_birth_aux();
1723 /* Note player birth in the message recall */
1724 message_add(" ", MSG_GENERIC);
1725 message_add(" ", MSG_GENERIC);
1726 message_add("====================", MSG_GENERIC);
1727 message_add(" ", MSG_GENERIC);
1728 message_add(" ", MSG_GENERIC);
1731 /* Hack -- outfit the player */
1732 player_outfit();
1735 /* Initialise the stores */
1736 store_init();