/* * create.c: create a player character * * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "constant.h" #include "types.h" #include "config.h" #include "externs.h" #ifdef USG #ifndef ATARIST_MWC #include #endif #else #include #endif /* Lets do all prototypes correctly.... -CWS */ #ifndef NO_LINT_ARGS #ifdef __STDC__ static void change_stat(int, int); static int monval(int); #else static void change_stat(); static int monval(); #endif static void get_stats(); static void set_prev_stats(); static int get_prev_stats(); static void get_all_stats(); static void put_auto_stats(); static void choose_race(); static void print_history(); static void set_prev_history(); static void get_prev_history(); static void get_sex(); static void get_ahw(); static void set_prev_ahw(); static void get_prev_ahw(); static void get_class(); static void get_class_choice(); static void get_money(); #endif struct previous { int16u age; int16u wt; int16u ht; int16 disarm; int16u stat[6]; int16u sc; char history[4][60]; background_type bg; } prev; extern int peek; /* Generates character's stats -JWT- */ static void get_stats() { register int i, tot; int dice[18]; do { tot = 0; for (i = 0; i < 18; i++) { dice[i] = randint(3 + i % 3); /* Roll 3,4,5 sided dice once each */ tot += dice[i]; } } while (tot <= 42 || tot >= 54); for (i = 0; i < 6; i++) py.stats.max_stat[i] = 5 + dice[3 * i] + dice[3 * i + 1] + dice[3 * i + 2]; } /* Returns adjusted stat -JK- */ /* Algorithm by ... -JWT- */ /* Used by change_stats and auto_roller * auto_roll is boolean and states maximum changes * should be used rather than random ones * to allow specification of higher values to wait for */ static int adjust_stat(stat_value, amount, auto_roll) int stat_value; int16 amount; int auto_roll; { register int i; if (amount < 0) for (i = 0; i > amount; i--) { /* OK so auto_roll conditions not needed for negative amounts since stat_value * is always 15 at least currently! -JK */ if (stat_value > 108) stat_value--; else if (stat_value > 88) stat_value -= ((auto_roll ? 6 : randint(6)) + 2); else if (stat_value > 18) { stat_value -= ((auto_roll ? 15 : randint(15)) + 5); if (stat_value < 18) stat_value = 18; } else if (stat_value > 3) stat_value--; } else for (i = 0; i < amount; i++) { if (stat_value < 18) stat_value++; else if (stat_value < 88) stat_value += ((auto_roll ? 15 : randint(15)) + 5); else if (stat_value < 108) stat_value += ((auto_roll ? 6 : randint(6)) + 2); else if (stat_value < 118) stat_value++; } return stat_value; } /* Changes stats by given amount -JWT- */ static void change_stat(stat, amount) int stat; int amount; { py.stats.max_stat[stat] = adjust_stat(py.stats.max_stat[stat], (int16) amount, FALSE); } static void set_prev_stats() { register int i; for (i = 0; i < 6; i++) prev.stat[i] = (int16u) py.stats.max_stat[i]; return; } static int get_prev_stats() { register int i; if (!prev.stat[0]) return 0; for (i = 0; i < 6; i++) { py.stats.cur_stat[i] = (py.stats.max_stat[i] = prev.stat[i]); py.stats.use_stat[i] = prev.stat[i]; } py.misc.ptodam = todam_adj(); py.misc.ptohit = tohit_adj(); py.misc.pac = toac_adj(); prev.stat[0] = 0; return 1; } /* * generate all stats and modify for race. needed in a separate module so * looping of character selection would be allowed -RGM- */ static void get_all_stats() { register player_type *p_ptr; register race_type *r_ptr; register int j; p_ptr = &py; r_ptr = &race[p_ptr->misc.prace]; get_stats(); change_stat(A_STR, r_ptr->str_adj); change_stat(A_INT, r_ptr->int_adj); change_stat(A_WIS, r_ptr->wis_adj); change_stat(A_DEX, r_ptr->dex_adj); change_stat(A_CON, r_ptr->con_adj); change_stat(A_CHR, r_ptr->chr_adj); for (j = 0; j < 6; j++) { py.stats.cur_stat[j] = py.stats.max_stat[j]; py.stats.use_stat[j] = modify_stat(j, py.stats.mod_stat[j]); } p_ptr->misc.srh = r_ptr->srh; p_ptr->misc.bth = r_ptr->bth; p_ptr->misc.bthb = r_ptr->bthb; p_ptr->misc.fos = r_ptr->fos; p_ptr->misc.stl = r_ptr->stl; p_ptr->misc.save = r_ptr->bsav; p_ptr->misc.hitdie = r_ptr->bhitdie; p_ptr->misc.lev = 1; p_ptr->misc.ptodam = todam_adj(); p_ptr->misc.ptohit = tohit_adj(); p_ptr->misc.ptoac = 0; p_ptr->misc.pac = toac_adj(); p_ptr->misc.expfact = r_ptr->b_exp; p_ptr->flags.see_infra = r_ptr->infra; } /* copied from misc2.c, so the display loop would work nicely -cft */ static const char *stat_names[] = {"STR: ", "INT: ", "WIS: ", "DEX: ", "CON: ", "CHR: "}; #ifdef AUTOROLLER /* used for auto-roller. Just put_stats(), w/o the extra info -CFT */ static void put_auto_stats() { register int i; vtype buf; for (i = 0; i < 6; i++) { cnv_stat(py.stats.use_stat[i], buf); put_buffer(stat_names[i], 2 + i, 61); put_buffer(buf, 2 + i, 66); if (py.stats.max_stat[i] > py.stats.cur_stat[i]) { cnv_stat(py.stats.max_stat[i], buf); put_buffer(buf, 2 + i, 73); } } } #endif /* Allows player to select a race -JWT- */ static void choose_race() { register int j, k; int l, m, exit_flag; char s; char tmp_str[80]; register player_type *p_ptr; register race_type *r_ptr; j = 0; k = 0; l = 2; m = 21; clear_from(20); put_buffer("Choose a race (? for Help):", 20, 2); do { (void)sprintf(tmp_str, "%c) %s", k + 'a', race[j].trace); put_buffer(tmp_str, m, l); k++; l += 15; if (l > 70) { l = 2; m++; } j++; } while (j < MAX_RACES); exit_flag = FALSE; do { move_cursor(20, 30); s = inkey(); j = s - 'a'; if ((j < MAX_RACES) && (j >= 0)) exit_flag = TRUE; else if (s == '?') helpfile(ANGBAND_WELCOME); else bell(); } while (!exit_flag); p_ptr = &py; r_ptr = &race[j]; p_ptr->misc.prace = j; put_buffer(r_ptr->trace, 3, 15); } /* Will print the history of a character -JWT- */ static void print_history() { register int i; put_buffer("Character Background", 14, 27); for (i = 0; i < 4; i++) prt(py.misc.history[i], i + 15, 10); } static void set_prev_history() { prev.bg.info = background->info; prev.bg.roll = background->roll; prev.bg.chart = background->chart; prev.bg.next = background->next; prev.bg.bonus = background->bonus; prev.sc = py.misc.sc; (void)strncpy(prev.history[0], py.misc.history[0], 60); (void)strncpy(prev.history[1], py.misc.history[1], 60); (void)strncpy(prev.history[2], py.misc.history[2], 60); (void)strncpy(prev.history[3], py.misc.history[3], 60); return; } static void get_prev_history() { register int i; background->info = prev.bg.info; background->roll = prev.bg.roll; background->chart = prev.bg.chart; background->next = prev.bg.next; background->bonus = prev.bg.bonus; py.misc.sc = prev.sc; for (i = 0; i < 4; i++) strncpy(py.misc.history[i], prev.history[i], 60); } /* Get the racial history, determines social class -RAK- */ /* Assumptions: Each race has init history beginning at * (race-1)*3+1 * All history parts are in ascending order */ static void get_history() { int hist_ptr, cur_ptr, test_roll, flag; register int start_pos, end_pos, cur_len; int line_ctr, new_start = 0, social_class; char history_block[240]; register background_type *b_ptr; /* Get a block of history text */ if (py.misc.prace == 8) hist_ptr = 1; else if (py.misc.prace > 8) hist_ptr = 2 * 3 + 1; else hist_ptr = py.misc.prace * 3 + 1; history_block[0] = '\0'; social_class = randint(4); cur_ptr = 0; do { flag = FALSE; do { if (background[cur_ptr].chart == hist_ptr) { test_roll = randint(100); while (test_roll > background[cur_ptr].roll) cur_ptr++; b_ptr = &background[cur_ptr]; (void)strcat(history_block, b_ptr->info); social_class += b_ptr->bonus - 50; if (hist_ptr > b_ptr->next) cur_ptr = 0; hist_ptr = b_ptr->next; flag = TRUE; } else cur_ptr++; } while (!flag); } while (hist_ptr >= 1); /* clear the previous history strings */ for (hist_ptr = 0; hist_ptr < 4; hist_ptr++) py.misc.history[hist_ptr][0] = '\0'; /* Process block of history text for pretty output */ start_pos = 0; end_pos = strlen(history_block) - 1; line_ctr = 0; flag = FALSE; while (history_block[end_pos] == ' ') end_pos--; do { while (history_block[start_pos] == ' ') start_pos++; cur_len = end_pos - start_pos + 1; if (cur_len > 60) { cur_len = 60; while (history_block[start_pos + cur_len - 1] != ' ') cur_len--; new_start = start_pos + cur_len; while (history_block[start_pos + cur_len - 1] == ' ') cur_len--; } else flag = TRUE; (void)strncpy(py.misc.history[line_ctr], &history_block[start_pos], cur_len); py.misc.history[line_ctr][cur_len] = '\0'; line_ctr++; start_pos = new_start; } while (!flag); /* Compute social class for player */ if (social_class > 100) social_class = 100; else if (social_class < 1) social_class = 1; py.misc.sc = social_class; } /* Gets the character's sex -JWT- */ static void get_sex() { register int exit_flag; char c; exit_flag = FALSE; clear_from(20); put_buffer("Choose a sex (? for Help):", 20, 2); put_buffer("m) Male f) Female", 21, 2); do { move_cursor(20, 29); /* speed not important here */ c = inkey(); if (c == 'f' || c == 'F') { py.misc.male = FALSE; put_buffer("Female", 4, 15); exit_flag = TRUE; } else if (c == 'm' || c == 'M') { py.misc.male = TRUE; put_buffer("Male", 4, 15); exit_flag = TRUE; } else if (c == '?') helpfile(ANGBAND_WELCOME); else bell(); } while (!exit_flag); } /* Computes character's age, height, and weight -JWT- */ static void get_ahw() { register int i; i = py.misc.prace; py.misc.age = race[i].b_age + randint((int)race[i].m_age); if (py.misc.male) { py.misc.ht = randnor((int)race[i].m_b_ht, (int)race[i].m_m_ht); py.misc.wt = randnor((int)race[i].m_b_wt, (int)race[i].m_m_wt); } else { py.misc.ht = randnor((int)race[i].f_b_ht, (int)race[i].f_m_ht); py.misc.wt = randnor((int)race[i].f_b_wt, (int)race[i].f_m_wt); } py.misc.disarm += race[i].b_dis; } static void set_prev_ahw() { prev.age = py.misc.age; prev.wt = py.misc.wt; prev.ht = py.misc.ht; prev.disarm = py.misc.disarm; return; } static void get_prev_ahw() { py.misc.age = prev.age; py.misc.wt = prev.wt; py.misc.ht = prev.ht; py.misc.disarm = prev.disarm; prev.age = prev.wt = prev.ht = prev.disarm = 0; } /* Gets a character class -JWT- */ static void get_class() { register int i; int min_value, max_value; int percent; char buf[50]; register struct misc *m_ptr; register player_type *p_ptr; class_type *c_ptr; c_ptr = &class[py.misc.pclass]; p_ptr = &py; change_stat(A_STR, c_ptr->madj_str); change_stat(A_INT, c_ptr->madj_int); change_stat(A_WIS, c_ptr->madj_wis); change_stat(A_DEX, c_ptr->madj_dex); change_stat(A_CON, c_ptr->madj_con); change_stat(A_CHR, c_ptr->madj_chr); for (i = 0; i < 6; i++) { p_ptr->stats.cur_stat[i] = p_ptr->stats.max_stat[i]; p_ptr->stats.use_stat[i] = p_ptr->stats.max_stat[i]; } p_ptr->misc.ptodam = todam_adj(); /* Real values */ p_ptr->misc.ptohit = tohit_adj(); p_ptr->misc.ptoac = toac_adj(); p_ptr->misc.pac = 0; p_ptr->misc.dis_td = p_ptr->misc.ptodam; /* Displayed values */ p_ptr->misc.dis_th = p_ptr->misc.ptohit; p_ptr->misc.dis_tac = p_ptr->misc.ptoac; p_ptr->misc.dis_ac = p_ptr->misc.pac + p_ptr->misc.dis_tac; /* now set misc stats, do this after setting stats because of con_adj() for * hitpoints */ m_ptr = &py.misc; m_ptr->hitdie += c_ptr->adj_hd; m_ptr->mhp = con_adj() + m_ptr->hitdie; m_ptr->chp = m_ptr->mhp; m_ptr->chp_frac = 0; /* initialize hit_points array: put bounds on total possible hp, * only succeed if it is within 1/8 of average value */ min_value = (MAX_PLAYER_LEVEL * 3 * (m_ptr->hitdie - 1)) / 8 + MAX_PLAYER_LEVEL; max_value = (MAX_PLAYER_LEVEL * 5 * (m_ptr->hitdie - 1)) / 8 + MAX_PLAYER_LEVEL; player_hp[0] = m_ptr->hitdie; do { for (i = 1; i < MAX_PLAYER_LEVEL; i++) { player_hp[i] = randint((int)m_ptr->hitdie); player_hp[i] += player_hp[i - 1]; } } while ((player_hp[MAX_PLAYER_LEVEL - 1] < min_value) || (player_hp[MAX_PLAYER_LEVEL - 1] > max_value)); if (peek) { percent = (int)(((long)player_hp[MAX_PLAYER_LEVEL - 1] * 200L) / (m_ptr->hitdie + ((MAX_PLAYER_LEVEL - 1) * m_ptr->hitdie))); sprintf(buf, "%d%% Life Rating", percent); msg_print(buf); } m_ptr->bth += c_ptr->mbth; m_ptr->bthb += c_ptr->mbthb; /* RAK */ m_ptr->srh += c_ptr->msrh; m_ptr->disarm = c_ptr->mdis + todis_adj(); m_ptr->fos += c_ptr->mfos; m_ptr->stl += c_ptr->mstl; m_ptr->save += c_ptr->msav; m_ptr->expfact += c_ptr->m_exp; } void rerate() { int min_value, max_value, i, percent; char buf[50]; struct misc *m_ptr = &py.misc; min_value = (MAX_PLAYER_LEVEL * 3 * (m_ptr->hitdie - 1)) / 8 + MAX_PLAYER_LEVEL; max_value = (MAX_PLAYER_LEVEL * 5 * (m_ptr->hitdie - 1)) / 8 + MAX_PLAYER_LEVEL; player_hp[0] = m_ptr->hitdie; do { for (i = 1; i < MAX_PLAYER_LEVEL; i++) { player_hp[i] = randint((int)m_ptr->hitdie); player_hp[i] += player_hp[i - 1]; } } while ((player_hp[MAX_PLAYER_LEVEL - 1] < min_value) || (player_hp[MAX_PLAYER_LEVEL - 1] > max_value)); percent = (int)(((long)player_hp[MAX_PLAYER_LEVEL - 1] * 200L) / (m_ptr->hitdie + ((MAX_PLAYER_LEVEL - 1) * m_ptr->hitdie))); sprintf(buf, "%d%% Life Rating", percent); calc_hitpoints(); prt_stat_block(); msg_print(buf); } /* Gets a character class -JWT- */ static void get_class_choice() { register int i, j; int k, l, m; int cl[MAX_CLASS], exit_flag; class_type *c_ptr; char tmp_str[80], s; int32u mask; for (j = 0; j < MAX_CLASS; j++) cl[j] = 0; i = py.misc.prace; j = 0; k = 0; l = 2; m = 21; mask = 0x1; clear_from(20); put_buffer("Choose a class (? for Help):", 20, 2); do { if (race[i].rtclass & mask) { (void)sprintf(tmp_str, "%c) %s", k + 'a', class[j].title); put_buffer(tmp_str, m, l); cl[k] = j; l += 15; if (l > 70) { l = 2; m++; } k++; } j++; mask <<= 1; } while (j < MAX_CLASS); py.misc.pclass = 0; exit_flag = FALSE; do { move_cursor(20, 31); s = inkey(); j = s - 'a'; if ((j < k) && (j >= 0)) { py.misc.pclass = cl[j]; c_ptr = &class[py.misc.pclass]; exit_flag = TRUE; clear_from(20); put_buffer(c_ptr->title, 5, 15); } else if (s == '?') helpfile(ANGBAND_WELCOME); else bell(); } while (!exit_flag); } /* Given a stat value, return a monetary value, which affects the amount of * gold a player has. */ static int monval(i) int i; { return 5 * ((int)i - 10); } static void get_money() { register int tmp, gold; register int16u *a_ptr; a_ptr = py.stats.max_stat; tmp = monval(a_ptr[A_STR]) + monval(a_ptr[A_INT]) + monval(a_ptr[A_WIS]) + monval(a_ptr[A_CON]) + monval(a_ptr[A_DEX]); gold = py.misc.sc * 6 + randint(25) + 325; /* Social Class adj */ gold -= tmp; /* Stat adj */ gold += monval(a_ptr[A_CHR]); /* Charisma adj */ if (!py.misc.male) gold += 50; /* She charmed the banker into it! -CJS- */ /* She slept with the banker.. :) -GDH- */ if (gold < 80) gold = 80; /* Minimum */ py.misc.au = gold; } /* ---------- M A I N for Character Creation Routine ---------- */ /* -JWT- */ void create_character() { register char c; #ifdef AUTOROLLER int32u auto_round = 0; register int i; int stat[6]; int autoroll = 0; int msstat = 0;/* Max autoroll w/ look for -SAC */ class_type *cptr; race_type *rptr; char inp[60]; #endif int previous_exists = 0; /* flag to prevent prev from garbage values */ town_seed = random(); /* Change random seeds for new characters -CWS */ randes_seed = random(); put_character(); choose_race(); get_sex(); get_class_choice(); #ifdef AUTOROLLER /* * This auto-roller stolen from a post on rec.games.moria, which I belive was * taken from druid moria 5.something. If this intrudes on someone's * copyright, take it out, and someone let me know -CFT */ put_buffer("Do you want to use automatic rolling? (? for Help) ", 20, 2); do { /* allow multiple key entry, so they can ask for help and * still get back to this menu... -CFT */ move_cursor(20, 52); c = inkey(); if (c == '?') helpfile(ANGBAND_WELCOME); } while ((c != 'y') && (c != 'Y') && (c != 'n') && (c != 'N')); if ((c == 'Y') || (c == 'y')) { autoroll = 1; clear_from(15); cptr = &class[py.misc.pclass]; rptr = &race[py.misc.prace]; put_buffer("Enter minimum attribute for: ", 15, 2); for (i = 0; i < 6; i++) { int stat_idx = 0; switch (i) { case 0: stat_idx = A_STR; clear_from(16 + i); msstat = adjust_stat(17, cptr->madj_str+rptr->str_adj, TRUE); sprintf(inp, " Strength (Max of %2d): ", msstat); put_buffer(inp, 16 + i, 5); break; case 1: stat_idx = A_INT; clear_from(16 + i); msstat = adjust_stat(17, cptr->madj_int+rptr->int_adj, TRUE); sprintf(inp, "Intelligence (Max of %2d): ", msstat); put_buffer(inp, 16 + i, 5); break; case 2: stat_idx = A_WIS; clear_from(16 + i); msstat = adjust_stat(17, cptr->madj_wis+rptr->wis_adj, TRUE); sprintf(inp, " Wisdom (Max of %2d): ", msstat); put_buffer(inp, 16 + i, 5); break; case 3: stat_idx = A_DEX; clear_from(16 + i); msstat = adjust_stat(17, cptr->madj_dex+rptr->dex_adj, TRUE); sprintf(inp, " Dexterity (Max of %2d): ", msstat); put_buffer(inp, 16 + i, 5); break; case 4: stat_idx = A_CON; clear_from(16 + i); msstat = adjust_stat(17, cptr->madj_con+rptr->con_adj, TRUE); sprintf(inp, "Constitution (Max of %2d): ", msstat); put_buffer(inp, 16 + i, 5); break; case 5: stat_idx = A_CHR; clear_from(16 + i); msstat = adjust_stat(17, cptr->madj_chr+rptr->chr_adj, TRUE); sprintf(inp, " Charisma (Max of %2d): ", msstat); put_buffer(inp, 16 + i, 5); break; } /* switch */ do { inp[0] = '\000'; get_string(inp, 16 + i, 32, 3); stat[stat_idx] = atoi(inp); /* have return give a stat of 3 */ if (inp[0] == '\015' || inp[0] == '\012' || inp[0] == '\000') stat[stat_idx] = 3; if (stat[stat_idx] < 0) { stat[stat_idx] = (-stat[stat_idx]); if (stat[stat_idx] > msstat) msstat = stat[stat_idx]; } } while (stat[stat_idx] > msstat || stat[stat_idx] < 3); } /* for i 0 - 5 */ put_qio(); } #endif /* AUTOROLLER - main setup code */ do { /* Main generation loop */ clear_from(9); #ifdef AUTOROLLER if (autoroll) for (i = 2; i < 6; i++) erase_line(i, 30); do { /* Start of AUTOROLLing loop */ #endif get_all_stats(); get_class(); #ifdef AUTOROLLER if (autoroll) { put_auto_stats(); auto_round++; sprintf(inp, "auto-rolling round #%lu.", (long)auto_round); put_buffer(inp, 20, 2); #if defined(unix) && defined(NICE) usleep((long)100000L); #endif put_qio(); } else put_stats(); } while ((autoroll) && ((stat[A_STR] > py.stats.cur_stat[A_STR]) || (stat[A_INT] > py.stats.cur_stat[A_INT]) || (stat[A_WIS] > py.stats.cur_stat[A_WIS]) || (stat[A_DEX] > py.stats.cur_stat[A_DEX]) || (stat[A_CON] > py.stats.cur_stat[A_CON]) || (stat[A_CHR] > py.stats.cur_stat[A_CHR])) #if (defined (unix) || defined(ATARI_ST)) /* CFT's if/elif/else */ && (!check_input(1))); /* unix needs flush here */ #elif (defined(MSDOS) || defined(VMS)) &&(!kbhit())); if (kbhit()) flush(); #else ); #endif /* character checks */ #endif /* AUTOROLLER main looping section */ get_history(); /* Common stuff */ get_ahw(); calc_bonuses(); print_history(); put_misc1(); clear_from(20); do { /* Input loop */ if (previous_exists) { put_buffer("Hit space: Reroll, ^P: Previous or ESC: Accept: ", 20, 2); move_cursor(20, 50); } else { put_buffer("Hit space: Reroll, or ESC: Accept: ", 20, 2); move_cursor(20, 37); } c = inkey(); if ((previous_exists) && (c == CTRL('P'))) { previous_exists = FALSE; if (get_prev_stats()) { get_prev_history(); get_prev_ahw(); print_history(); put_misc1(); calc_bonuses(); put_stats(); clear_from(20); } } else if ((c != ' ') && (c != ESCAPE)) /* Prolly better way to do this */ bell(); } while ((c != ' ') && (c != ESCAPE)); /* Not going to waste space w/ a check here. So ESC takes a little longer. -SAC */ set_prev_stats(); set_prev_history(); set_prev_ahw(); previous_exists = TRUE; } while (c == ' '); get_money(); put_stats(); put_misc2(); put_misc3(); get_name(); msg_print(NULL); /* This delay may be reduced, but is recommended to keep players * from continuously rolling up characters, which can be VERY * expensive CPU wise. */ pause_exit(23, PLAYER_EXIT_PAUSE); }