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.
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.
45 * The last character displayed
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)
65 /*** Save the current data ***/
68 prev
.age
= p_ptr
->age
;
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)
95 /*** Save the current data ***/
98 temp
.age
= p_ptr
->age
;
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 ***/
117 p_ptr
->age
= prev
.age
;
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 ***/
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
));
182 for (i
= 0; i
< amount
; i
++)
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)
203 /* Return the result */
211 * Roll for a characters stats
213 * For efficiency, we include a chunk of "calc_bonuses()".
215 static void get_stats(void)
224 /* Roll and verify some stats */
228 for (j
= i
= 0; i
< 18; i
++)
231 dice
[i
] = randint(3 + i
% 3);
233 /* Collect the maximum */
238 if ((j
> 42) && (j
< 54)) break;
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 */
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 */
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
;
285 p_ptr
->max_lev
= p_ptr
->lev
= 1;
287 /* Experience factor */
288 p_ptr
->expfact
= rp_ptr
->r_exp
+ cp_ptr
->c_exp
;
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 */
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;
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);
345 chart
= rp_ptr
->hist
;
348 /* Process the history */
354 /* Roll for nobility */
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)
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;
439 * Clear all the global "character" data
441 static void player_wipe(bool really_wipe
)
445 byte psex
, prace
, pclass
;
455 /* Backup the player choices */
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 */
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
];
484 /* Start with no quests */
485 for (i
= 0; i
< MAX_Q_IDX
; i
++)
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
];
503 k_ptr
->tried
= FALSE
;
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 */
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 */
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)
552 const start_item
*e_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 */
569 /* Get the object_kind */
570 int k_idx
= lookup_kind(e_ptr
->tval
, e_ptr
->sval
);
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
);
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);
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;
610 k_info
[i_ptr
->k_idx
].everseen
= TRUE
;
611 (void)inven_carry(i_ptr
);
615 /* Locations of the tables on the screen */
617 #define QUESTION_ROW 7
620 #define QUESTION_COL 2
623 #define RACE_AUX_COL 29
625 #define CLASS_AUX_COL 50
628 * Clear the previous question
630 static void clear_question(void)
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
)
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
)
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') {
708 else if (cmd
== '*') {
713 if(mask
& (1L << oid
)) break;
721 else if (cmd
== KTRL('X'))
725 else if (cmd
== '?') {
727 strnfmt(buf
, sizeof(buf
), "%s#%s", "birth.txt", topic
);
729 show_file(buf
, NULL
, 0, 0);
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
;
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
);
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
);
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
);
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
];
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
808 static bool roller_handler(char cmd
, void *db
, int oid
)
810 if (cmd
== '\xff' || cmd
== '\r')
822 else if(cmd
== KTRL('X'))
824 else if(cmd
== '?') {
833 str
= "Standard roller";
835 strnfmt(buf
, sizeof(buf
), "%s#%s", "birth.txt", str
);
837 show_file(buf
, NULL
, 0, 0);
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 */
861 static bool choose_character(bool start_at_end
)
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 }; */
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
;
893 menu
.cmd_keys
= "?=*\r\n\x18"; /* ?, ,= *, \n, <ctl-X> */
894 menu
.selections
= lower_case
;
896 while (i
< (int)N_ELEMENTS(menu_defs
))
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
]);
907 Term_putstr(QUESTION_COL
, QUESTION_ROW
, -1, TERM_YELLOW
, hints
[i
]);
913 if (i
== N_ELEMENTS(menu_defs
) - 1)
915 start_at_end
= FALSE
;
920 cx
= menu_select(&menu
, &cursor
, 0);
922 if (cx
.key
== ESCAPE
|| cx
.type
== EVT_BACK
)
926 /* Move back one menu */
928 region_erase(regions
[i
]);
932 else if (cx
.key
== '*')
940 else if(cx
.key
== '\r' || cx
.key
== '\n' || cx
.key
== '\xff')
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
)
959 /*** Instructions ***/
964 /* Output to the screen */
965 text_out_hook
= text_out_to_screen
;
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 */
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;
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
)
1045 static int stats
[A_MAX
];
1053 bool first_time
= FALSE
;
1062 /* Initialize stats */
1063 for (i
= 0; i
< A_MAX
; i
++)
1070 /* Roll for base hitpoints */
1073 /* Roll for age/height/weight */
1076 /* Roll for social class */
1087 for (i
= 0; i
< A_MAX
; i
++)
1089 /* Variable stat maxes */
1093 p_ptr
->stat_cur
[i
] = p_ptr
->stat_max
[i
] = stats
[i
];
1097 /* Fixed stat maxes */
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
);
1109 cost
+= birth_stat_costs
[stats
[i
] - 10];
1116 bell("Excessive stats!");
1121 /* Recompute costs */
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
);
1135 p_ptr
->chp
= p_ptr
->mhp
;
1138 p_ptr
->csp
= p_ptr
->msp
;
1140 /* Display the player */
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
++)
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
);
1159 /* Place cursor just after cost of current stat */
1160 Term_gotoxy(col
+ 36, row
+ stat
);
1165 if (ch
== KTRL('X'))
1168 /* Go back a step, or back to the start of this step */
1180 if ((ch
== '\r') || (ch
== '\n')) break;
1182 ch
= target_dir(ch
);
1187 stat
= (stat
+ A_MAX
- 1) % A_MAX
;
1193 stat
= (stat
+ 1) % A_MAX
;
1197 if ((ch
== 4) && (stats
[stat
] > 10))
1203 if ((ch
== 6) && (stats
[stat
] < 18))
1210 /* Done - advance a step*/
1215 bool minstat_keypress(char *buf
, size_t buflen
, size_t *curs
, size_t *len
, char keypress
, bool firsttime
)
1217 if (keypress
== KTRL('x'))
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
)
1234 static bool prev
= FALSE
;
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;
1260 if (!start_at_end
&& autoroll
)
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 */
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 */
1296 /* Extract a textual format */
1297 /* cnv_stat(m, inp, sizeof(buf); */
1302 strnfmt(inp
, sizeof(inp
), "(Max of 18/%02d):", (m
- 18));
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 */
1326 /* Move the cursor */
1327 put_str("", 16 + i
, 30);
1332 /* Get a response (or escape) */
1333 if (!askfor_aux(inp
, 9, minstat_keypress
))
1339 /* Repeat this step */
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 */
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;
1384 put_str(" Limit", 2, col
+5);
1387 put_str(" Freq", 2, col
+13);
1390 put_str(" Roll", 2, col
+24);
1392 /* Put the minimal stats */
1393 for (i
= 0; i
< A_MAX
; i
++)
1396 put_str(stat_names
[i
], 3+i
, col
);
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
;
1407 put_str("Round:", 10, col
+13);
1409 /* Indicate the state */
1410 put_str("(Hit ESC to stop)", 12, col
+13);
1417 /* Get a new character */
1420 /* Advance the 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
])
1435 /* This stat is not okay */
1442 /* Break if "happy" */
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
++)
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 */
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 */
1470 c_put_str(TERM_RED
, "(NONE)", 3+i
, col
+13);
1475 put_str(format("%10ld", auto_round
), 10, col
+20);
1477 /* Make sure they see everything */
1480 /* Do not wait for a key */
1483 /* Check for a keypress */
1489 /* Otherwise just get a character */
1492 /* Get a new character */
1502 /* Roll for base hitpoints */
1505 /* Roll for age/height/weight */
1508 /* Roll for social class */
1515 start_at_end
= FALSE
;
1520 /* Calculate the bonuses and hitpoints */
1521 p_ptr
->update
|= (PU_BONUS
| PU_HP
);
1527 p_ptr
->chp
= p_ptr
->mhp
;
1530 p_ptr
->csp
= p_ptr
->msp
;
1532 /* Display the player */
1535 /* Prepare a prompt (must squeeze everything in) */
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 */
1546 /* Go back to the start of the step, or the previous step */
1547 /* if we're not autorolling. */
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'))
1576 if (ch
== KTRL('X'))
1580 bell("Illegal auto-roller command!");
1584 if ((ch
== '\r') || (ch
== '\n')) break;
1586 /* Save this for the "previous" character */
1589 /* Note that a previous roll exists */
1596 /* Done - move on a stage */
1606 BIRTH_FINAL_APPROVAL
,
1612 * Helper function for 'player_birth()'.
1614 * See "display_player" for screen layout code.
1616 static void player_birth_aux(void)
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
;
1625 bool start_at_end
= (last_state
> state
);
1636 case BIRTH_QUESTIONS
:
1638 /* Race, class, etc. choices */
1639 if (player_birth_aux_1(start_at_end
))
1646 if (roller_type
== ROLLER_POINT
)
1648 /* Fill stats using point-based methods */
1649 state
+= player_birth_aux_2(start_at_end
);
1653 /* Fills stats using the standard- or auto-roller */
1654 state
+= player_birth_aux_3(start_at_end
, roller_type
== ROLLER_AUTO
);
1661 /* Get a name, prepare savefile */
1662 if (get_name(FALSE
))
1670 case BIRTH_FINAL_APPROVAL
:
1672 /* Display the player */
1676 prt(prompt
, Term
->hgt
- 1, Term
->wid
/ 2 - strlen(prompt
) / 2);
1683 state
= BIRTH_RESTART
;
1699 case BIRTH_ACCEPTED
:
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 */
1720 /* Create a new character */
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 */
1735 /* Initialise the stores */