/* File: birth.c */ /* Purpose: 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 "angband.h" /* * How often the autoroller will update the display and pause * to check for user interuptions. * Bigger values will make the autoroller faster, but slower * system may have problems because the user can't stop the * autoroller for this number of rolls. */ #define AUTOROLLER_STEP 25L /* * Maximum number of tries for selection of a proper quest monster */ #define MAX_TRIES 100 /* Max quests */ static byte max_quests = 0; /* * Current stats */ static s16b stat_use[6]; /* * Autoroll limit */ static s16b stat_limit[6]; /* * Autoroll matches */ static s32b stat_match[6]; /* * Autoroll round */ static s32b auto_round; /* * Last round */ static s32b last_round; /* Human */ static char *human_syllable1[] = { "Ab", "Ac", "Ad", "Af", "Agr", "Ast", "As", "Al", "Adw", "Adr", "Ar", "B", "Br", "C", "Cr", "Ch", "Cad", "D", "Dr", "Dw", "Ed", "Eth", "Et", "Er", "El", "Eow", "F", "Fr", "G", "Gr", "Gw", "Gal", "Gl", "H", "Ha", "Ib", "Jer", "K", "Ka", "Ked", "L", "Loth", "Lar", "Leg", "M", "Mir", "N", "Nyd", "Ol", "Oc", "On", "P", "Pr", "R", "Rh", "S", "Sev", "T", "Tr", "Th", "V", "Y", "Z", "W", "Wic", }; static char *human_syllable2[] = { "a", "ae", "au", "ao", "are", "ale", "ali", "ay", "ardo", "e", "ei", "ea", "eri", "era", "ela", "eli", "enda", "erra", "i", "ia", "ie", "ire", "ira", "ila", "ili", "ira", "igo", "o", "oa", "oi", "oe", "ore", "u", "y", }; static char *human_syllable3[] = { "a", "and", "b", "bwyn", "baen", "bard", "c", "ctred", "cred", "ch", "can", "d", "dan", "don", "der", "dric", "dfrid", "dus", "f", "g", "gord", "gan", "l", "li", "lgrin", "lin", "lith", "lath", "loth", "ld", "ldric", "ldan", "m", "mas", "mos", "mar", "mond", "n", "nydd", "nidd", "nnon", "nwan", "nyth", "nad", "nn", "nnor", "nd", "p", "r", "ron", "rd", "s", "sh", "seth", "sean", "t", "th", "tha", "tlan", "trem", "tram", "v", "vudd", "w", "wan", "win", "wyn", "wyr", "wyr", "wyth", }; /* * Random Name Generator * based on a Javascript by Michael Hensley * "http://geocities.com/timessquare/castle/6274/" */ static void create_random_name(int race, char *name) { char *syl1, *syl2, *syl3; int idx; /* Paranoia */ if (!name) return; /* Select the monster type */ switch (race) { /* Create the monster name */ /* Use human ones */ default: { idx = rand_int(sizeof(human_syllable1) / sizeof(char *)); syl1 = human_syllable1[idx]; idx = rand_int(sizeof(human_syllable2) / sizeof(char *)); syl2 = human_syllable2[idx]; idx = rand_int(sizeof(human_syllable3) / sizeof(char *)); syl3 = human_syllable3[idx]; break; } } /* Concatenate selected syllables */ strnfmt(name, 32, "%s%s%s", syl1, syl2, syl3); } void print_desc_aux(cptr txt, int y, int xx) { int i = -1, x = xx; while (txt[++i] != 0) { if (txt[i] == '\n') { x = xx; y++; } else { Term_putch(x++, y, TERM_YELLOW, txt[i]); } } } void print_desc(cptr txt) { print_desc_aux(txt, 12, 1); } /* * Save the current data for later */ static void save_prev_data(void) { int i; /*** Save the current data ***/ /* Save the data */ previous_char.sex = p_ptr->psex; previous_char.race = p_ptr->prace; previous_char.rmod = p_ptr->pracem; previous_char.pclass = p_ptr->pclass; previous_char.spec = p_ptr->pspec; previous_char.quests = max_quests; previous_char.god = p_ptr->pgod; previous_char.grace = p_ptr->grace; previous_char.age = p_ptr->age; previous_char.wt = p_ptr->wt; previous_char.ht = p_ptr->ht; previous_char.sc = p_ptr->sc; previous_char.au = p_ptr->au; /* Save the stats */ for (i = 0; i < 6; i++) { previous_char.stat[i] = p_ptr->stat_max[i]; } previous_char.luck = p_ptr->luck_base; /* Save the chaos patron */ previous_char.chaos_patron = p_ptr->chaos_patron; /* Save the weapon specialty */ previous_char.weapon = 0; /* Save the history */ for (i = 0; i < 4; i++) { strcpy(previous_char.history[i], history[i]); } } /* * Load the previous data */ static void load_prev_data(bool_ save) { int i; birther temp; /*** Save the current data ***/ /* Save the data */ temp.age = p_ptr->age; temp.wt = p_ptr->wt; temp.ht = p_ptr->ht; temp.sc = p_ptr->sc; temp.au = p_ptr->au; /* Save the stats */ for (i = 0; i < 6; i++) { temp.stat[i] = p_ptr->stat_max[i]; } temp.luck = p_ptr->luck_base; /* Save the chaos patron */ temp.chaos_patron = p_ptr->chaos_patron; /* Save the weapon specialty */ temp.weapon = 0; /* Save the history */ for (i = 0; i < 4; i++) { strcpy(temp.history[i], history[i]); } /*** Load the previous data ***/ /* Load the data */ p_ptr->age = previous_char.age; p_ptr->wt = previous_char.wt; p_ptr->ht = previous_char.ht; p_ptr->sc = previous_char.sc; p_ptr->au = previous_char.au; /* Load the stats */ for (i = 0; i < 6; i++) { p_ptr->stat_max[i] = previous_char.stat[i]; p_ptr->stat_cur[i] = previous_char.stat[i]; } p_ptr->luck_base = previous_char.luck; p_ptr->luck_max = previous_char.luck; /* Load the chaos patron */ p_ptr->chaos_patron = previous_char.chaos_patron; /* Load the history */ for (i = 0; i < 4; i++) { strcpy(history[i], previous_char.history[i]); } /*** Save the current data ***/ if (!save) return; /* Save the data */ previous_char.age = temp.age; previous_char.wt = temp.wt; previous_char.ht = temp.ht; previous_char.sc = temp.sc; previous_char.au = temp.au; /* Save the stats */ for (i = 0; i < 6; i++) { previous_char.stat[i] = temp.stat[i]; } previous_char.luck = temp.luck; /* Save the chaos patron */ previous_char.chaos_patron = temp.chaos_patron; /* Save the weapon specialty */ previous_char.weapon = temp.weapon; /* Save the history */ for (i = 0; i < 4; i++) { strcpy(previous_char.history[i], temp.history[i]); } } /* * Returns adjusted stat -JK- Algorithm by -JWT- * * auto_roll is boolean and states maximum changes should be used rather * than random ones to allow specification of higher values to wait for * * The "p_ptr->maximize" code is important -BEN- */ static int adjust_stat(int value, int amount, int auto_roll) { int i; /* Negative amounts */ if (amount < 0) { /* Apply penalty */ for (i = 0; i < (0 - amount); i++) { if (value >= 18 + 10) { value -= 10; } else if (value > 18) { value = 18; } else if (value > 3) { value--; } } } /* Positive amounts */ else if (amount > 0) { /* Apply reward */ for (i = 0; i < amount; i++) { if (value < 18) { value++; } else if (p_ptr->maximize) { value += 10; } else if (value < 18 + 70) { value += ((auto_roll ? 15 : randint(15)) + 5); } else if (value < 18 + 90) { value += ((auto_roll ? 6 : randint(6)) + 2); } else if (value < 18 + 100) { value++; } } } /* Return the result */ return (value); } /* * Roll for a characters stats * * For efficiency, we include a chunk of "calc_bonuses()". */ static void get_stats(void) { int i, j; int bonus; int dice[18]; /* Roll and verify some stats */ while (TRUE) { /* Roll some dice */ for (j = i = 0; i < 18; i++) { /* Roll the dice */ dice[i] = randint(3 + i % 3); /* Collect the maximum */ j += dice[i]; } /* * Verify totals * * 57 was 54... I hate 'magic numbers' :< TY * * (d3 + d4 + d5) ~= 7.5 (+- 4.5) * with 5 makes avg. stat value of 12.5 (min 8, max 17) * * (d3 + d4 + d5) x 6 ~= 45 (+- 18) * * So the original value (still used by Vanilla as of 2.9.3) * allows (avg - 2)..(avg + 8), while this Z version * (avg - 2)..(avg + 11). I don't understand what TY meant * by "magic numbers", but I like big stats :) -- pelpel * */ if ((j > 42) && (j < 57)) break; } /* Acquire the stats */ for (i = 0; i < 6; i++) { /* Extract 5 + 1d3 + 1d4 + 1d5 */ j = 5 + dice[3 * i] + dice[3 * i + 1] + dice[3 * i + 2]; /* Save that value */ p_ptr->stat_max[i] = j; /* Obtain a "bonus" for "race" and "class" */ bonus = rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i]; /* Variable stat maxes */ if (p_ptr->maximize) { /* Start fully healed */ p_ptr->stat_cur[i] = p_ptr->stat_max[i]; /* Efficiency -- Apply the racial/class bonuses */ stat_use[i] = modify_stat_value(p_ptr->stat_max[i], bonus); } /* Fixed stat maxes */ else { /* Apply the bonus to the stat (somewhat randomly) */ stat_use[i] = adjust_stat(p_ptr->stat_max[i], bonus, FALSE); /* Save the resulting stat maximum */ p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stat_use[i]; } /* No temporary drain (yet...) */ p_ptr->stat_cnt[i] = 0; p_ptr->stat_los[i] = 0; } /* Get luck */ p_ptr->luck_base = rp_ptr->luck + rmp_ptr->luck + rand_range( -5, 5); p_ptr->luck_max = p_ptr->luck_base; } /* * Roll for some info that the auto-roller ignores */ static void get_extra(void) { int i, j, min_value, max_value; #ifdef SHOW_LIFE_RATE int percent; #endif /* Level one */ p_ptr->max_plv = p_ptr->lev = 1; /* Experience factor */ p_ptr->expfact = rp_ptr->r_exp + rmp_ptr->r_exp + cp_ptr->c_exp; /* Initialize arena and rewards information -KMW- */ p_ptr->arena_number = 0; p_ptr->inside_arena = 0; p_ptr->inside_quest = 0; p_ptr->exit_bldg = TRUE; /* only used for arena now -KMW- */ /* Hitdice */ p_ptr->hitdie = rp_ptr->r_mhp + rmp_ptr->r_mhp + cp_ptr->c_mhp; /* Initial hitpoints */ p_ptr->mhp = p_ptr->hitdie; /* Minimum hitpoints at highest level */ min_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 3) / 8; min_value += PY_MAX_LEVEL; /* Maximum hitpoints at highest level */ max_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 5) / 8; max_value += PY_MAX_LEVEL; /* Pre-calculate level 1 hitdice */ player_hp[0] = p_ptr->hitdie; /* Roll out the hitpoints */ while (TRUE) { /* Roll the hitpoint values */ for (i = 1; i < PY_MAX_LEVEL; i++) { j = randint(p_ptr->hitdie); player_hp[i] = player_hp[i - 1] + j; } /* XXX Could also require acceptable "mid-level" hitpoints */ /* Require "valid" hitpoints at highest level */ if (player_hp[PY_MAX_LEVEL - 1] < min_value) continue; if (player_hp[PY_MAX_LEVEL - 1] > max_value) continue; /* Acceptable */ break; } p_ptr->tactic = 4; p_ptr->movement = 4; #ifdef SHOW_LIFE_RATE percent = (int)(((long)player_hp[PY_MAX_LEVEL - 1] * 200L) / (p_ptr->hitdie + ((PY_MAX_LEVEL - 1) * p_ptr->hitdie))); msg_format("Current Life Rating is %d/100.", percent); msg_print(NULL); #endif /* SHOW_LIFE_RATE */ } /* * Get the racial history, and social class, using the "history charts". */ static void get_history(void) { int i, n, chart, roll, social_class; char *s, *t; char buf[240]; /* Clear the previous history strings */ for (i = 0; i < 4; i++) history[i][0] = '\0'; /* Clear the history text */ buf[0] = '\0'; /* Initial social class */ social_class = randint(4); /* Starting place */ chart = rp_ptr->chart; /* Process the history */ while (chart) { /* Start over */ i = 0; /* Roll for nobility */ roll = randint(100); /* Access the proper entry in the table */ while ((chart != bg[i].chart) || (roll > bg[i].roll)) i++; /* Acquire the textual history */ (void)strcat(buf, bg[i].info + rp_text); /* Add in the social class */ social_class += (int)(bg[i].bonus) - 50; /* Enter the next chart */ chart = bg[i].next; } /* Verify social class */ if (social_class > 100) social_class = 100; else if (social_class < 1) social_class = 1; /* Save the social class */ p_ptr->sc = social_class; /* Skip leading spaces */ for (s = buf; *s == ' '; s++) /* loop */; /* Get apparent length */ n = strlen(s); /* Kill trailing spaces */ while ((n > 0) && (s[n - 1] == ' ')) s[--n] = '\0'; /* Start at first line */ i = 0; /* Collect the history */ while (TRUE) { /* Extract remaining length */ n = strlen(s); /* All done */ if (n < 60) { /* Save one line of history */ strcpy(history[i++], s); /* All done */ break; } /* Find a reasonable break-point */ for (n = 60; ((n > 0) && (s[n - 1] != ' ')); n--) /* loop */; /* Save next location */ t = s + n; /* Wipe trailing spaces */ while ((n > 0) && (s[n - 1] == ' ')) s[--n] = '\0'; /* Save one line of history */ strcpy(history[i++], s); /* Start next line */ for (s = t; *s == ' '; s++) /* loop */; } } /* * Fill the random_artifacts array with relevant info. */ errr init_randart(void) { int i; long cost; random_artifact* ra_ptr; char buf[80]; for (i = 0; i < MAX_RANDARTS; i++) { ra_ptr = &random_artifacts[i]; strcpy(ra_ptr->name_short, get_line("rart_s.txt", ANGBAND_DIR_FILE, buf, i)); strcpy(ra_ptr->name_full, get_line("rart_f.txt", ANGBAND_DIR_FILE, buf, i)); ra_ptr->attr = randint(15); ra_ptr->activation = rand_int(MAX_T_ACT); ra_ptr->generated = FALSE; cost = randnor(0, 250); if (cost < 0) cost = 0; ra_ptr->cost = cost; } return 0; } /* * A helper function for get_ahw(), also called by polymorph code */ void get_height_weight(void) { int h_mean, h_stddev; int w_mean, w_stddev; /* Extract mean and standard deviation -- Male */ if (p_ptr->psex == SEX_MALE) { h_mean = rp_ptr->m_b_ht + rmp_ptr->m_b_ht; h_stddev = rp_ptr->m_m_ht + rmp_ptr->m_m_ht; w_mean = rp_ptr->m_b_wt + rmp_ptr->m_b_wt; w_stddev = rp_ptr->m_m_wt + rmp_ptr->m_m_wt; } /* Female */ else if (p_ptr->psex == SEX_FEMALE) { h_mean = rp_ptr->f_b_ht + rmp_ptr->f_b_ht; h_stddev = rp_ptr->f_m_ht + rmp_ptr->f_m_ht; w_mean = rp_ptr->f_b_wt + rmp_ptr->f_b_wt; w_stddev = rp_ptr->f_m_wt + rmp_ptr->f_m_wt; } /* Neuter XXX */ else { h_mean = (rp_ptr->m_b_ht + rmp_ptr->m_b_ht + rp_ptr->f_b_ht + rmp_ptr->f_b_ht) / 2, h_stddev = (rp_ptr->m_m_ht + rmp_ptr->m_m_ht + rp_ptr->f_m_ht + rmp_ptr->f_m_ht) / 2; w_mean = (rp_ptr->m_b_wt + rmp_ptr->m_b_wt + rp_ptr->f_b_wt + rmp_ptr->f_b_wt) / 2, w_stddev = (rp_ptr->m_m_wt + rmp_ptr->m_m_wt + rp_ptr->f_m_wt + rmp_ptr->f_m_wt) / 2; } /* Calculate height/weight */ p_ptr->ht = randnor(h_mean, h_stddev); p_ptr->wt = randnor(w_mean, w_stddev); /* Weight/height shouldn't be negative */ if (p_ptr->ht < 1) p_ptr->ht = 1; if (p_ptr->wt < 1) p_ptr->wt = 1; } /* * Computes character's age, height, and weight */ static void get_ahw(void) { /* Calculate the age */ p_ptr->age = rp_ptr->b_age + rmp_ptr->b_age + randint(rp_ptr->m_age + rmp_ptr->m_age); /* Calculate the height/weight */ get_height_weight(); } /* * Get the player's starting money */ static void get_money(void) { int i, gold; /* Social Class determines starting gold */ gold = (p_ptr->sc * 6) + randint(100) + 300; /* Process the stats */ for (i = 0; i < 6; i++) { /* Mega-Hack -- reduce gold for high stats */ if (stat_use[i] >= 18 + 50) gold -= 300; else if (stat_use[i] >= 18 + 20) gold -= 200; else if (stat_use[i] > 18) gold -= 150; else gold -= (stat_use[i] - 8) * 10; } /* Minimum 100 gold */ if (gold < 100) gold = 100; /* Save the gold */ p_ptr->au = gold; } /* * Display stat values, subset of "put_stats()" * * See 'display_player()' for basic method. */ static void birth_put_stats(void) { int i, p; byte attr; char buf[80]; /* Put the stats (and percents) */ for (i = 0; i < 6; i++) { /* Put the stat */ cnv_stat(p_ptr->stat_use[i], buf); c_put_str(TERM_L_GREEN, buf, 2 + i, 66); /* Put the percent */ if (stat_match[i]) { p = 1000L * stat_match[i] / auto_round; attr = (p < 100) ? TERM_YELLOW : TERM_L_GREEN; strnfmt(buf, 80, "%3d.%d%%", p / 10, p % 10); c_put_str(attr, buf, 2 + i, 73); } /* Never happened */ else { c_put_str(TERM_RED, "(NONE)", 2 + i, 73); } } } /* * Clear all the global "character" data */ static void player_wipe(void) { int i, j; bool_ *powers; bool_ *corruptions; /* Wipe special levels */ wipe_saved(); /* Save the powers & corruptions */ powers = p_ptr->powers; corruptions = p_ptr->corruptions; /* Hack -- zero the struct */ p_ptr = WIPE(p_ptr, player_type); /* Restore the powers & corruptions */ p_ptr->powers = powers; p_ptr->corruptions = corruptions; /* Not dead yet */ p_ptr->lives = 0; /* Wipe the corruptions */ (void)C_WIPE(p_ptr->corruptions, max_corruptions, bool_); /* Wipe the history */ for (i = 0; i < 4; i++) { for (j = 0; j < 60; j++) { if (j < 59) history[i][j] = ' '; else history[i][j] = '\0'; } } /* Wipe the towns */ for (i = 0; i < max_d_idx; i++) { for (j = 0; j < MAX_DUNGEON_DEPTH; j++) { special_lvl[j][i] = 0; } } /* Wipe the towns */ for (i = max_real_towns + 1; i < max_towns; i++) { town_info[i].flags = 0; } /* Wipe the quests */ for (i = 0; i < MAX_Q_IDX_INIT; i++) { quest[i].status = QUEST_STATUS_UNTAKEN; for (j = 0; j < 4; j++) { quest[i].data[j] = 0; } } /* Wipe the rune spells */ rune_num = 0; for (i = 0; i < MAX_RUNES; i++) { strcpy(rune_spells[i].name, ""); rune_spells[i].type = 0; rune_spells[i].rune2 = 0; rune_spells[i].mana = 0; } /* No items */ inven_cnt = 0; equip_cnt = 0; /* Clear the inventory */ for (i = 0; i < INVEN_TOTAL; i++) { object_wipe(&p_ptr->inventory[i]); } /* Generate random artifacts */ init_randart(); /* Start with no artifacts made yet */ for (i = 0; i < max_a_idx; i++) { artifact_type *a_ptr = &a_info[i]; a_ptr->cur_num = 0; } /* Reset the "objects" */ for (i = 1; i < max_k_idx; i++) { object_kind *k_ptr = &k_info[i]; /* Reset "tried" */ k_ptr->tried = FALSE; /* Reset "aware" */ k_ptr->aware = FALSE; /* Reset "know" */ k_ptr->know = FALSE; /* Reset "artifact" */ k_ptr->artifact = 0; } /* Reset the "monsters" */ for (i = 1; i < max_r_idx; i++) { monster_race *r_ptr = &r_info[i]; /* Hack -- Reset the counter */ r_ptr->cur_num = 0; /* Hack -- Reset the max counter */ r_ptr->max_num = 100; /* Hack -- Reset the max counter */ if (r_ptr->flags1 & RF1_UNIQUE) r_ptr->max_num = 1; if (r_ptr->flags3 & RF3_UNIQUE_4) r_ptr->max_num = 4; /* Clear player kills */ r_ptr->r_pkills = 0; /* Clear saved flag */ r_ptr->on_saved = FALSE; } /* Hack -- Well fed player */ p_ptr->food = PY_FOOD_FULL - 1; /* Wipe the alchemists' recipes */ for ( i = 0 ; i < 32 ; i++) alchemist_known_egos[i] = 0; for ( i = 0 ; i < 6 ; i++) alchemist_known_artifacts[i] = 0; alchemist_gained = 0; /* Clear "cheat" options */ cheat_peek = FALSE; cheat_hear = FALSE; cheat_room = FALSE; cheat_xtra = FALSE; cheat_know = FALSE; cheat_live = FALSE; /* Assume no winning game */ total_winner = 0; has_won = FALSE; /* Assume no cheating */ noscore = 0; wizard = 0; /* Assume no innate spells */ spell_num = 0; /* Clear the fate */ for (i = 0; i < MAX_FATES; i++) { fates[i].fate = 0; } p_ptr->no_mortal = FALSE; /* Player don't have the black breath from the beginning !*/ p_ptr->black_breath = FALSE; /* Default pet command settings */ p_ptr->pet_follow_distance = 6; p_ptr->pet_open_doors = FALSE; p_ptr->pet_pickup_items = FALSE; /* Body changing initialisation */ p_ptr->body_monster = 0; p_ptr->disembodied = FALSE; /* Wipe the bounties */ total_bounties = 0; /* Wipe spells */ p_ptr->xtra_spells = 0; /* Wipe xtra hp */ p_ptr->hp_mod = 0; /* Wipe the monsters */ wipe_m_list(); /* Wipe the doppleganger */ doppleganger = 0; /* Wipe the recall depths */ for (i = 0; i < max_d_idx; i++) { max_dlv[i] = 0; } /* Wipe the known inscription list */ for (i = 0; i < MAX_INSCRIPTIONS; i++) { inscription_info[i].know = FALSE; } /* Wipe the known traps list */ for (i = 0; i < max_t_idx; i++) { t_info[i].known = 0; t_info[i].ident = FALSE; } /* Reset wild_mode to FALSE */ p_ptr->wild_mode = FALSE; p_ptr->old_wild_mode = FALSE; /* Initialize allow_one_death */ p_ptr->allow_one_death = 0; p_ptr->loan = p_ptr->loan_time = 0; /* Wipe the power list */ for (i = 0; i < POWER_MAX_INIT; i++) { p_ptr->powers_mod[i] = 0; } /* No companions killed */ p_ptr->companion_killed = 0; } /* Create an object */ void outfit_obj(int tv, int sv, int pval, int dd, int ds) { object_type forge; object_type *q_ptr; /* Get local object */ q_ptr = &forge; q_ptr->pval = 0; q_ptr->pval2 = 0; /* Hack -- Give the player an object */ object_prep(q_ptr, lookup_kind(tv, sv)); if (pval) q_ptr->pval = pval; /* These objects are "storebought" */ q_ptr->ident |= IDENT_MENTAL; q_ptr->number = damroll(dd, ds); object_aware(q_ptr); object_known(q_ptr); (void)inven_carry(q_ptr, FALSE); } /* * Init players with some belongings * * Having an item makes the player "aware" of its purpose. */ static void player_outfit(void) { int i; object_type forge; object_type *q_ptr; /* * Get an adventurer guide describing a bit of the * wilderness(useless for vanilla town) */ if (!vanilla_town) { /* Get local object */ q_ptr = &forge; /* Hack -- Give the player an adventurer guide */ object_prep(q_ptr, lookup_kind(TV_PARCHMENT, 20)); q_ptr->number = 1; object_aware(q_ptr); object_known(q_ptr); (void)inven_carry(q_ptr, FALSE); } process_hooks(HOOK_BIRTH_OBJECTS, "()"); /* Get local object */ q_ptr = &forge; /* Get local object */ q_ptr = &forge; { /* Hack -- Give the player some food */ object_prep(q_ptr, lookup_kind(TV_FOOD, SV_FOOD_RATION)); q_ptr->number = (byte)rand_range(3, 7); object_aware(q_ptr); object_known(q_ptr); (void)inven_carry(q_ptr, FALSE); } /* Get local object */ q_ptr = &forge; { /* Hack -- Give the player some torches */ object_prep(q_ptr, lookup_kind(TV_LITE, SV_LITE_TORCH)); q_ptr->number = (byte)rand_range(3, 7); q_ptr->timeout = rand_range(3, 7) * 500; object_aware(q_ptr); object_known(q_ptr); (void)inven_carry(q_ptr, FALSE); } /* Get local object */ q_ptr = &forge; /* Rogues have a better knowledge of traps */ if (has_ability(AB_TRAPPING)) { t_info[TRAP_OF_DAGGER_I].known = randint(50) + 50; t_info[TRAP_OF_POISON_NEEDLE].known = randint(50) + 50; t_info[TRAP_OF_FIRE_BOLT].known = randint(50) + 50; t_info[TRAP_OF_DAGGER_I].ident = TRUE; t_info[TRAP_OF_POISON_NEEDLE].ident = TRUE; t_info[TRAP_OF_FIRE_BOLT].ident = TRUE; /* Hack -- Give the player a some ammo for the traps */ object_prep(q_ptr, lookup_kind(TV_SHOT, SV_AMMO_NORMAL)); q_ptr->number = (byte)rand_range(5, 15); object_aware(q_ptr); object_known(q_ptr); /* These objects are "storebought" */ q_ptr->ident |= IDENT_MENTAL; (void)inven_carry(q_ptr, FALSE); } /* Hack -- Give the player some useful objects */ for (i = 0; i < rp_ptr->obj_num; i++) outfit_obj(rp_ptr->obj_tval[i], rp_ptr->obj_sval[i], rp_ptr->obj_pval[i], rp_ptr->obj_dd[i], rp_ptr->obj_ds[i]); for (i = 0; i < rmp_ptr->obj_num; i++) outfit_obj(rmp_ptr->obj_tval[i], rmp_ptr->obj_sval[i], rmp_ptr->obj_pval[i], rmp_ptr->obj_dd[i], rmp_ptr->obj_ds[i]); for (i = 0; i < cp_ptr->obj_num; i++) outfit_obj(cp_ptr->obj_tval[i], cp_ptr->obj_sval[i], cp_ptr->obj_pval[i], cp_ptr->obj_dd[i], cp_ptr->obj_ds[i]); for (i = 0; i < cp_ptr->spec[p_ptr->pspec].obj_num; i++) outfit_obj(cp_ptr->spec[p_ptr->pspec].obj_tval[i], cp_ptr->spec[p_ptr->pspec].obj_sval[i], cp_ptr->spec[p_ptr->pspec].obj_pval[i], cp_ptr->spec[p_ptr->pspec].obj_dd[i], cp_ptr->spec[p_ptr->pspec].obj_ds[i]); } /* Possible number(and layout) or random quests */ #define MAX_RANDOM_QUESTS_TYPES ((8 * 3) + (8 * 1)) int random_quests_types[MAX_RANDOM_QUESTS_TYPES] = { 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */ 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */ 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */ 20, 13, 15, 16, 9, 17, 18, 8, /* Hero Sword Quest */ }; /* Enforce OoD monsters until this level */ #define RQ_LEVEL_CAP 49 static void gen_random_quests(int n) { int step, lvl, i, k; int old_type = dungeon_type; /* Factor dlev value by 1000 to keep precision */ step = (98 * 1000) / n; lvl = step / 2; quest[QUEST_RANDOM].status = QUEST_STATUS_TAKEN; for (i = 0; i < n; i++) { monster_race *r_ptr = &r_info[2]; int rl = (lvl / 1000) + 1; int min_level; int tries = 5000; random_quest *q_ptr = &random_quests[rl]; int j; /* Find the appropriate dungeon */ for (j = 0; j < max_d_idx; j++) { dungeon_info_type *d_ptr = &d_info[j]; if (!(d_ptr->flags1 & DF1_PRINCIPAL)) continue; if ((d_ptr->mindepth <= rl) && (rl <= d_ptr->maxdepth)) { dungeon_type = j; break; } } q_ptr->type = random_quests_types[rand_int(MAX_RANDOM_QUESTS_TYPES)]; /* XXX XXX XXX Try until valid choice is found */ while (tries) { bool_ ok; tries--; /* Random monster 5 - 10 levels out of depth */ q_ptr->r_idx = get_mon_num(rl + 4 + randint(6)); if (!q_ptr->r_idx) continue; r_ptr = &r_info[q_ptr->r_idx]; /* Accept only monsters that can be generated */ if (r_ptr->flags9 & RF9_SPECIAL_GENE) continue; if (r_ptr->flags9 & RF9_NEVER_GENE) continue; /* Accept only monsters that are not breeders */ if (r_ptr->flags4 & RF4_MULTIPLY) continue; /* Forbid joke monsters */ if (r_ptr->flags8 & RF8_JOKEANGBAND) continue; /* Accept only monsters that are not friends */ if (r_ptr->flags7 & RF7_PET) continue; /* Refuse nazguls */ if (r_ptr->flags7 & RF7_NAZGUL) continue; /* Accept only monsters that are not good */ if (r_ptr->flags3 & RF3_GOOD) continue; /* Assume no explosion attacks */ ok = TRUE; /* Reject monsters with exploding attacks */ for (k = 0; k < 4; k++) { if (r_ptr->blow[k].method == RBM_EXPLODE) ok = FALSE; } if (!ok) continue; /* No mutliple uniques */ if ((r_ptr->flags1 & RF1_UNIQUE) && ((q_ptr->type != 1) || (r_ptr->max_num == -1))) continue; /* No single non uniques */ if ((!(r_ptr->flags1 & RF1_UNIQUE)) && (q_ptr->type == 1)) continue; /* Level restriction */ min_level = (rl > RQ_LEVEL_CAP) ? RQ_LEVEL_CAP : rl; /* Accept monsters matching the level restriction */ if (r_ptr->level > min_level) break; } /* Arg could not find anything ??? */ if (!tries) { if (wizard) message_add(MESSAGE_MSG, format("Could not find quest monster on lvl %d", rl), TERM_RED); q_ptr->type = 0; } else { if (r_ptr->flags1 & RF1_UNIQUE) { r_ptr->max_num = -1; } q_ptr->done = FALSE; if (wizard) message_add(MESSAGE_MSG, format("Quest for %d on lvl %d", q_ptr->r_idx, rl), TERM_RED); } lvl += step; } dungeon_type = old_type; } int dump_classes(s16b *classes, int sel, u32b *restrictions) { int n = 0; char buf[80]; char *desc; cptr str; C_MAKE(desc, c_head->text_size, char); /* Clean up */ clear_from(12); while (classes[n] != -1) { cptr mod = ""; char p2 = ')', p1 = ' '; /* Analyze */ p_ptr->pclass = classes[n]; cp_ptr = &class_info[p_ptr->pclass]; str = cp_ptr->title + c_name; if (sel == n) { p1 = '['; p2 = ']'; } /* Display */ strnfmt(buf, 80, "%c%c%c %s%s", p1, (n <= 25) ? I2A(n) : I2D(n - 26), p2, str, mod); /* Print some more info */ if (sel == n) { strnfmt(desc, c_head->text_size, "%s%s", cp_ptr->desc + c_text, cp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : ""); print_desc(desc); if (!(restrictions[classes[n] / 32] & BIT(classes[n])) || cp_ptr->flags1 & PR1_EXPERIMENTAL) c_put_str(TERM_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); else c_put_str(TERM_L_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); } else { if (!(restrictions[classes[n] / 32] & BIT(classes[n])) || cp_ptr->flags1 & PR1_EXPERIMENTAL) c_put_str(TERM_SLATE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); else put_str(buf, 18 + (n / 4), 1 + 20 * (n % 4)); } n++; } C_FREE(desc, c_head->text_size, char); return (n); } int dump_specs(int sel) { int n = 0; char buf[80]; char *desc; cptr str; C_MAKE(desc, c_head->text_size, char); /* Clean up */ clear_from(12); for (n = 0; n < MAX_SPEC; n++) { char p2 = ')', p1 = ' '; /* Found the last one ? */ if (!class_info[p_ptr->pclass].spec[n].title) break; /* Analyze */ p_ptr->pspec = n; spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec]; str = spp_ptr->title + c_name; if (sel == n) { p1 = '['; p2 = ']'; } /* Display */ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str); /* Print some more info */ if (sel == n) { strnfmt(desc, c_head->text_size, "%s%s", spp_ptr->desc + c_text, spp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : ""); print_desc(desc); if (spp_ptr->flags1 & PR1_EXPERIMENTAL) c_put_str(TERM_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); else c_put_str(TERM_L_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); } else { if (spp_ptr->flags1 & PR1_EXPERIMENTAL) c_put_str(TERM_SLATE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); else put_str(buf, 18 + (n / 4), 1 + 20 * (n % 4)); } } C_FREE(desc, c_head->text_size, char); return (n); } int dump_races(int sel) { int n = 0; char buf[80]; char *desc; cptr str; C_MAKE(desc, rp_head->text_size, char); /* Clean up */ clear_from(12); for (n = 0; n < max_rp_idx; n++) { char p2 = ')', p1 = ' '; /* Analyze */ p_ptr->prace = n; rp_ptr = &race_info[p_ptr->prace]; str = rp_ptr->title + rp_name; if (sel == n) { p1 = '['; p2 = ']'; } /* Display */ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str); /* Print some more info */ if (sel == n) { strnfmt(desc, rp_head->text_size, "%s%s", rp_ptr->desc + rp_text, rp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : ""); print_desc(desc); if (rp_ptr->flags1 & PR1_EXPERIMENTAL) c_put_str(TERM_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); else c_put_str(TERM_L_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); } else { if (rp_ptr->flags1 & PR1_EXPERIMENTAL) c_put_str(TERM_SLATE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); else put_str(buf, 18 + (n / 5), 1 + 15 * (n % 5)); } } C_FREE(desc, rp_head->text_size, char); return (n); } int dump_rmods(int sel, int *racem, int max) { int n = 0; char buf[80]; char *desc; cptr str; C_MAKE(desc, rmp_head->text_size, char); /* Clean up */ clear_from(12); /* Dump races */ for (n = 0; n < max; n++) { char p2 = ')', p1 = ' '; /* Analyze */ p_ptr->pracem = racem[n]; rmp_ptr = &race_mod_info[p_ptr->pracem]; str = rmp_ptr->title + rmp_name; if (sel == n) { p1 = '['; p2 = ']'; } /* Display */ if (racem[n]) strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str); else strnfmt(buf, 80, "%c%c%c Classical", p1, I2A(n), p2); /* Print some more info */ if (sel == n) { strnfmt(desc, rmp_head->text_size, "%s%s", rmp_ptr->desc + rmp_text, rmp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : ""); print_desc(desc); if (rmp_ptr->flags1 & PR1_EXPERIMENTAL) c_put_str(TERM_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); else c_put_str(TERM_L_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); } else { if (rmp_ptr->flags1 & PR1_EXPERIMENTAL) c_put_str(TERM_SLATE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); else put_str(buf, 18 + (n / 5), 1 + 15 * (n % 5)); } } C_FREE(desc, rmp_head->text_size, char); return (n); } int dump_gods(int sel, int *choice, int max) { int i, j; char buf[80]; cptr str; /* Clean up */ clear_from(12); Term_putstr(5, 17, -1, TERM_WHITE, "You can choose to worship a god, some class must start with a god."); for (i = 0; i < max; i++) { char p2 = ')', p1 = ' '; int n = choice[i]; deity_type *g_ptr = &deity_info[0]; if (!n) str = "No God"; else { g_ptr = &deity_info[n]; str = g_ptr->name; } if (sel == i) { p1 = '['; p2 = ']'; } /* Display */ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(i), p2, str); /* Print some more info */ if (sel == i) { if (n) { /* Display the first four lines of the god description */ for (j = 0; j < 4; j++) if (strcmp(g_ptr->desc[j], "")) print_desc_aux(g_ptr->desc[j], 12 + j, 1); } else print_desc("You can begin as an atheist and still convert to a god later."); c_put_str(TERM_L_BLUE, buf, 20 + (i / 4), 1 + 20 * (i % 4)); } else { put_str(buf, 20 + (i / 4), 1 + 20 * (i % 4)); } } return (max); } /* Ask questions */ static bool_ do_quick_start = FALSE; static bool_ player_birth_aux_ask() { int i, k, n, v, sel; s32b tmp; int racem[100], max_racem = 0; u32b restrictions[2]; cptr str; char c; char p2 = ')'; char buf[200]; char inp[200]; s16b *class_types; s32b allow_quest; /*** Intro ***/ /* Clear screen */ Term_clear(); /* Title everything */ put_str("Name :", 2, 1); put_str("Sex :", 3, 1); put_str("Race :", 4, 1); put_str("Class :", 5, 1); /* Dump the default name */ c_put_str(TERM_L_BLUE, player_name, 2, 9); /*** Instructions ***/ /* Display some helpful information */ Term_putstr(5, 8, -1, TERM_WHITE, "Please answer the following questions. Most of the questions"); Term_putstr(5, 9, -1, TERM_WHITE, "display a set of standard answers, and many will also accept"); Term_putstr(5, 10, -1, TERM_WHITE, "some special responses, including 'Q' to quit, 'S' to restart,"); Term_putstr(5, 11, -1, TERM_WHITE, "and '?' for help. Note that 'Q' and 'S' must be capitalized."); /*** Quick Start ***/ if (previous_char.quick_ok) { /* Extra info */ Term_putstr(1, 15, -1, TERM_WHITE, "Do you want to use the quick start function(same character as your last one)."); /* Choose */ while (1) { put_str("Use quick start (y/n)?", 20, 2); c = inkey(); if (c == 'Q') quit(NULL); else if (c == 'S') return (FALSE); else if ((c == 'y') || (c == 'Y')) { do_quick_start = TRUE; break; } else { do_quick_start = FALSE; break; } } } /* Clean up */ clear_from(15); /*** Player sex ***/ if (do_quick_start) { k = previous_char.sex; } else { /* Extra info */ Term_putstr(5, 15, -1, TERM_WHITE, "Your 'sex' does not have any significant gameplay effects."); /* Prompt for "Sex" */ for (n = 0; n < MAX_SEXES; n++) { /* Analyze */ p_ptr->psex = n; sp_ptr = &sex_info[p_ptr->psex]; str = sp_ptr->title; /* Display */ strnfmt(buf, 200, "%c%c %s", I2A(n), p2, str); put_str(buf, 21 + (n / 5), 2 + 15 * (n % 5)); } /* Choose */ while (1) { strnfmt(buf, 200, "Choose a sex (%c-%c), * for random, = for options: ", I2A(0), I2A(n - 1)); put_str(buf, 20, 2); c = inkey(); if (c == 'Q') quit(NULL); if (c == 'S') return (FALSE); if (c == '*') { k = rand_int(MAX_SEXES); break; } k = (islower(c) ? A2I(c) : -1); if ((k >= 0) && (k < n)) break; if (c == '?') do_cmd_help(); else if (c == '=') { screen_save(); do_cmd_options_aux(6, "Startup Options", FALSE); screen_load(); } else bell(); } } /* Set sex */ p_ptr->psex = k; sp_ptr = &sex_info[p_ptr->psex]; str = sp_ptr->title; /* Display */ c_put_str(TERM_L_BLUE, str, 3, 9); /* Clean up */ clear_from(15); /*** Player race ***/ if (do_quick_start) { k = previous_char.race; } else { /* Only one choice = instant choice */ if (max_rp_idx == 1) k = 0; else { /* Extra info */ Term_putstr(5, 16, -1, TERM_WHITE, "Your 'race' determines various intrinsic factors and bonuses."); hack_corruption = FALSE; /* Dump races */ sel = 0; n = dump_races(sel); /* Choose */ while (1) { strnfmt(buf, 200, "Choose a race (%c-%c), * for a random choice, = for options, 8/2/4/6 for movement: ", I2A(0), I2A(max_rp_idx - 1)); put_str(buf, 17, 2); c = inkey(); if (c == 'Q') quit(NULL); if (c == 'S') return (FALSE); if (c == '*') { k = rand_int(max_rp_idx); break; } k = (islower(c) ? A2I(c) : -1); if ((k >= 0) && (k < n)) break; if (c == '?') exec_lua(format("ingame_help('select_context', 'race', '%s')", race_info[sel].title + rp_name)); else if (c == '=') { screen_save(); do_cmd_options_aux(6, "Startup Options", FALSE); screen_load(); } else if (c == '2') { sel += 5; if (sel >= n) sel %= 5; dump_races(sel); } else if (c == '8') { sel -= 5; if (sel < 0) sel = n - 1 -( ( -sel) % 5); /* C's modulus operator does not have defined results for negative first values. Damn. */ dump_races(sel); } else if (c == '6') { sel++; if (sel >= n) sel = 0; dump_races(sel); } else if (c == '4') { sel--; if (sel < 0) sel = n - 1; dump_races(sel); } else if (c == '\r') { k = sel; break; } else bell(); } } } /* Set race */ p_ptr->prace = k; rp_ptr = &race_info[p_ptr->prace]; str = rp_ptr->title + rp_name; /* Display */ c_put_str(TERM_L_BLUE, str, 4, 9); /* Get a random name */ if (!do_quick_start) create_random_name(p_ptr->prace, player_name); /* Display */ c_put_str(TERM_L_BLUE, player_name, 2, 9); /* Clean up */ clear_from(12); /*** Player race mod ***/ if (do_quick_start) { k = previous_char.rmod; p_ptr->pracem = k; rmp_ptr = &race_mod_info[p_ptr->pracem]; } else { /* Only one choice = instant choice */ if (max_rmp_idx == 1) k = 0; else { for (n = 0; n < 100; n++) racem[n] = 0; max_racem = 0; for (n = 0; n < max_rmp_idx; n++) { /* Analyze */ p_ptr->pracem = n; rmp_ptr = &race_mod_info[p_ptr->pracem]; /* Must be an ok choice */ if (!(BIT(p_ptr->prace) & rmp_ptr->choice[p_ptr->prace / 32])) continue; /* Ok thats a possibility */ racem[max_racem++] = n; } /* Ah ! nothing found, lets use the default */ if (!max_racem) p_ptr->pracem = 0; /* Only one ? use it */ else if (max_racem == 1) p_ptr->pracem = racem[0]; /* We got to ask the player */ else { /* Extra info */ Term_putstr(5, 15, -1, TERM_WHITE, "Your 'race modifier' determines various intrinsic factors and bonuses."); /* Dump races */ sel = 0; n = dump_rmods(sel, racem, max_racem); /* Choose */ while (1) { strnfmt(buf, 200, "Choose a race modifier (%c-%c), * for a random choice, = for options: ", I2A(0), I2A(max_racem - 1)); put_str(buf, 17, 2); c = inkey(); if (c == 'Q') quit(NULL); if (c == 'S') return (FALSE); if (c == '*') { do { k = rand_int(max_racem); } while (!(BIT(racem[k]) & rmp_ptr->choice[racem[k] / 32])); break; } else if (c == '?') exec_lua(format("ingame_help('select_context', 'subrace', '%s')", race_mod_info[racem[sel]].title + rmp_name)); k = (islower(c) ? A2I(c) : -1); if ((k >= 0) && (k < max_racem) && (BIT(p_ptr->prace) & race_mod_info[racem[k]].choice[p_ptr->prace / 32])) break; else if (c == '=') { screen_save(); do_cmd_options_aux(6, "Startup Options", FALSE); screen_load(); } else if (c == '2') { sel += 5; if (sel >= n) sel = sel - n + 1; dump_rmods(sel, racem, max_racem); } else if (c == '8') { sel -= 5; if (sel < 0) sel = n - 1 + sel; dump_rmods(sel, racem, max_racem); } else if (c == '6') { sel++; if (sel >= n) sel = 0; dump_rmods(sel, racem, max_racem); } else if (c == '4') { sel--; if (sel < 0) sel = n - 1; dump_rmods(sel, racem, max_racem); } else if (c == '\r') { k = sel; break; } else bell(); } /* Set race */ p_ptr->pracem = racem[k]; } rmp_ptr = &race_mod_info[p_ptr->pracem]; /* Display */ c_put_str(TERM_L_BLUE, get_player_race_name(p_ptr->prace, p_ptr->pracem), 4, 9); } } /* Clean up */ clear_from(12); /*** Player class ***/ if (do_quick_start) { k = previous_char.pclass; p_ptr->pclass = k; cp_ptr = &class_info[p_ptr->pclass]; k = previous_char.spec; p_ptr->pspec = k; spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec]; } else { int z; for (z = 0; z < 2; z++) restrictions[z] = (rp_ptr->choice[z] | rmp_ptr->pclass[z]) & (~rmp_ptr->mclass[z]); if (max_mc_idx > 1) { /* Extra info */ Term_putstr(5, 13, -1, TERM_WHITE, "Your 'class' determines various intrinsic abilities and bonuses."); /* Get a class type */ for (i = 0; i < max_mc_idx; i++) c_put_str(meta_class_info[i].color, format("%c) %s", I2A(i), meta_class_info[i].name), 16 + i, 2); while (1) { strnfmt(buf, 200, "Choose a class type (a-%c), * for random, = for options: ", I2A(max_mc_idx - 1)); put_str(buf, 15, 2); c = inkey(); if (c == 'Q') quit(NULL); if (c == 'S') return (FALSE); if (c == '*') { k = rand_int(max_mc_idx); break; } k = (islower(c) ? A2I(c) : (D2I(c) + 26)); if ((k >= 0) && (k < max_mc_idx)) break; if (c == '?') do_cmd_help(); else if (c == '=') { screen_save(); do_cmd_options_aux(6, "Startup Options", FALSE); screen_load(); } else bell(); } } else { k = 0; } class_types = meta_class_info[k].classes; clear_from(15); /* Count classes */ n = 0; while (class_types[n] != -1) n++; /* Only one choice = instant choice */ if (n == 1) k = 0; else { /* Dump classes */ sel = 0; n = dump_classes(class_types, sel, restrictions); /* Get a class */ while (1) { strnfmt(buf, 200, "Choose a class (%c-%c), * for random, = for options, 8/2/4 for up/down/back: ", I2A(0), (n <= 25) ? I2A(n - 1) : I2D(n - 26-1)); put_str(buf, 15, 2); c = inkey(); if (c == 'Q') quit(NULL); if (c == 'S') return (FALSE); if (c == '*') { k = randint(n) - 1; break; } k = (islower(c) ? A2I(c) : (D2I(c) + 26)); if ((k >= 0) && (k < n)) break; if (c == '?') exec_lua(format("ingame_help('select_context', 'class', '%s')", class_info[class_types[sel]].title + c_name)); else if (c == '=') { screen_save(); do_cmd_options_aux(6, "Startup Options", FALSE); screen_load(); } else if (c == '2') { sel += 4; if (sel >= n) sel %= 4; dump_classes(class_types, sel, restrictions); } else if (c == '8') { sel -= 4; if (sel < 0) sel = n - 1 -( ( -sel) % 4); /* C's modulus operator does not have defined results for negative first values. Damn. */ dump_classes(class_types, sel, restrictions); } else if (c == '6') { sel++; if (sel >= n) sel = 0; dump_classes(class_types, sel, restrictions); } else if (c == '4') { sel--; if (sel < 0) sel = n - 1; dump_classes(class_types, sel, restrictions); } else if (c == '\r') { k = sel; break; } else bell(); } } /* Set class */ p_ptr->pclass = class_types[k]; /* Choose class spec */ clear_from(15); /* Count choices */ for (n = 0; n < MAX_SPEC; n++) { /* Found the last one ? */ if (!class_info[p_ptr->pclass].spec[n].title) break; } /* Only one choice = auto choice */ if (n == 1) k = 0; else { /* Dump classes spec */ sel = 0; n = dump_specs(sel); /* Get a class */ while (1) { strnfmt(buf, 200, "Choose a class specialisation (%c-%c), * for random, = for options, 8/2/4/6 for up/down/left/right: ", I2A(0), (n <= 25) ? I2A(n - 1) : I2D(n - 26-1)); put_str(buf, 15, 2); c = inkey(); if (c == 'Q') quit(NULL); if (c == 'S') return (FALSE); if (c == '*') { k = randint(n) - 1; break; } k = (islower(c) ? A2I(c) : (D2I(c) + 26)); if ((k >= 0) && (k < n)) break; if (c == '?') exec_lua(format("ingame_help('select_context', 'class', '%s')", class_info[p_ptr->pclass].spec[sel].title + c_name)); else if (c == '=') { screen_save(); do_cmd_options_aux(6, "Startup Options", FALSE); screen_load(); } else if (c == '2') { sel += 4; if (sel >= n) sel = sel - n + 1; dump_specs(sel); } else if (c == '8') { sel -= 4; if (sel < 0) sel = n - 1 + sel; dump_specs(sel); } else if (c == '6') { sel++; if (sel >= n) sel = 0; dump_specs(sel); } else if (c == '4') { sel--; if (sel < 0) sel = n - 1; dump_specs(sel); } else if (c == '\r') { k = sel; break; } else bell(); } } /* Set class spec */ p_ptr->pspec = k; } cp_ptr = &class_info[p_ptr->pclass]; spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec]; str = spp_ptr->title + c_name; /* Display */ c_put_str(TERM_L_BLUE, str, 5, 9); /* Clean up */ clear_from(15); /*** Player god ***/ if (do_quick_start) { k = previous_char.god; p_ptr->pgod = k; set_grace(previous_char.grace); } else if (PRACE_FLAG(PR1_NO_GOD)) { p_ptr->pgod = GOD_NONE; } else { int *choice; int max = 0; C_MAKE(choice, max_gods, int); /* Get the list of possible gods */ for (n = 0; n < max_gods; n++) { if ((cp_ptr->gods | spp_ptr->gods) & BIT(n)) choice[max++] = n; } if (!max) { p_ptr->pgod = GOD_NONE; } else if (max == 1) { p_ptr->pgod = choice[0]; } else if (max > 1) { sel = 0; n = dump_gods(sel, choice, max); /* Choose */ while (1) { strnfmt(buf, 200, "Choose a god (%c-%c), * for a random choice, " "= for options, 8/2/4/6 for movement: ", I2A(0), I2A(max - 1)); put_str(buf, 19, 2); c = inkey(); if (c == 'Q') quit(NULL); if (c == 'S') { C_FREE(choice, max_gods, int); return (FALSE); } if (c == '*') { k = choice[randint(max) - 1]; break; } k = (islower(c) ? A2I(c) : -1); if ((k >= 0) && (k < max)) { k = choice[k]; break; } if (c == '?') exec_lua(format("ingame_help('select_context', 'god', '%s')", deity_info[choice[sel]].name)); else if (c == '=') { screen_save(); do_cmd_options_aux(6, "Startup Options", FALSE); screen_load(); } else if (c == '2') { sel += 4; if (sel >= n) sel %= 4; dump_gods(sel, choice, max); } else if (c == '8') { sel -= 4; /* C's modulus operator does not have defined results for negative first values. Damn. */ if (sel < 0) sel = n - 1 -( ( -sel) % 4); dump_gods(sel, choice, max); } else if (c == '6') { sel++; if (sel >= n) sel = 0; dump_gods(sel, choice, max); } else if (c == '4') { sel--; if (sel < 0) sel = n - 1; dump_gods(sel, choice, max); } else if (c == '\r') { k = choice[sel]; break; } else bell(); } C_FREE(choice, max_gods, int); /* Set god */ p_ptr->pgod = k; p_ptr->grace = 0; } /* A god that like us ? more grace ! */ if (PRACE_FLAGS(PR1_GOD_FRIEND)) { set_grace(200); } else { set_grace(100); } } /* Clean up */ clear_from(12); if (!do_quick_start) { /* Clear */ clear_from(15); /* */ if (get_check("Do you want to modify the options")) { screen_save(); do_cmd_options_aux(6, "Startup Options", FALSE); screen_load(); } } /* Set birth options: maximize, preserve, sepcial levels and astral */ p_ptr->maximize = maximize; p_ptr->preserve = preserve; p_ptr->special = special_lvls; p_ptr->astral = (PRACE_FLAG2(PR2_ASTRAL)) ? TRUE : FALSE; /* * A note by pelpel. (remove this please) * Be it the new Vanilla way (adult vs. birth options) or * the old one (player_type members), it would be less confusing * to handle birth-only options in a uniform fashion,the above and * the following: * ironman_rooms, * joke_monsters, * always_small_level, and * fate_option */ /* Set the recall dungeon accordingly */ call_lua("get_module_info", "(s)", "d", "base_dungeon", &tmp); dungeon_type = tmp; p_ptr->recall_dungeon = dungeon_type; max_dlv[dungeon_type] = d_info[dungeon_type].mindepth; if (p_ptr->astral) { s32b x, y, astral_dun; call_lua("get_module_info", "(s)", "d", "astral_dungeon", &astral_dun); dungeon_type = astral_dun; /* Somewhere in the misty mountains */ call_lua("get_module_info", "(s)", "d", "astral_wild_x", &x); call_lua("get_module_info", "(s)", "d", "astral_wild_y", &y); p_ptr->wilderness_x = x; p_ptr->wilderness_y = y; } /* Clean up */ clear_from(10); /*** User enters number of quests ***/ /* Heino Vander Sanden and Jimmy De Laet */ call_lua("get_module_info", "(s)", "d", "rand_quest", &allow_quest); if (!ironman_rooms && allow_quest) { if (do_quick_start) { v = previous_char.quests; } else { /* Extra info */ Term_putstr(5, 15, -1, TERM_WHITE, "Select the number of optional random quests you'd like to receive."); Term_putstr(5, 16, -1, TERM_WHITE, "If you do not want any optional quests, enter 0."); /* Ask the number of additional quests */ while (TRUE) { put_str(format("Number of quests? (0-%u) ", MAX_RANDOM_QUEST - 1), 20, 2); /* Get a the number of additional quest */ while (TRUE) { /* Move the cursor */ put_str("", 20, 27); /* Default */ strcpy(inp, "20"); /* Get a response (or escape) */ if (!askfor_aux(inp, 2)) inp[0] = '\0'; if (inp[0] == '*') v = rand_int(MAX_RANDOM_QUEST); else v = atoi(inp); /* Break on valid input */ if ((v < MAX_RANDOM_QUEST) && ( v >= 0 )) break; } break; } /* Clear */ clear_from(15); } } else { /* NO quests for ironman rooms or persistent levels, since they don't work */ v = 0; } /* Set the quest monster hook */ get_mon_num_hook = monster_quest; /* Prepare allocation table */ get_mon_num_prep(); /* Generate quests */ for (i = 0; i < MAX_RANDOM_QUEST; i++) random_quests[i].type = 0; if (v) gen_random_quests(v); max_quests = v; p_ptr->inside_quest = 0; /* Init the plots */ call_lua("get_module_info", "(s)", "d", "C_quest", &allow_quest); if (allow_quest) { plots[PLOT_MAIN] = QUEST_NECRO; quest[plots[PLOT_MAIN]].status = QUEST_STATUS_TAKEN; plots[PLOT_BREE] = QUEST_THIEVES; quest[plots[PLOT_BREE]].status = QUEST_STATUS_UNTAKEN; plots[PLOT_LORIEN] = QUEST_WOLVES; quest[plots[PLOT_LORIEN]].status = QUEST_STATUS_UNTAKEN; plots[PLOT_GONDOLIN] = QUEST_DRAGONS; quest[plots[PLOT_GONDOLIN]].status = QUEST_STATUS_UNTAKEN; plots[PLOT_MINAS] = QUEST_HAUNTED; quest[plots[PLOT_MINAS]].status = QUEST_STATUS_UNTAKEN; plots[PLOT_KHAZAD] = QUEST_EVIL; quest[plots[PLOT_KHAZAD]].status = QUEST_STATUS_UNTAKEN; plots[PLOT_OTHER] = QUEST_NULL; } quest_random_init_hook(QUEST_RANDOM); /* Ok */ return (TRUE); } /* * Initial stat costs (initial stats always range from 10 to 18 inclusive). */ static const int birth_stat_costs[(18-10) + 1] = { 0, 1, 2, 4, 7, 11, 16, 22, 30 }; /* * Helper function for 'player_birth()'. * * This function handles "point-based" character creation. * * The player selects, for each stat, a value from 10 to 18 (inclusive), * each costing a certain amount of points (as above), from a pool of 48 * available points, to which race/class modifiers are then applied. * * Each unused point is converted into 100 gold pieces, with a maximum of * 600 gp at birth. * * Taken from V 2.9.0 */ static bool_ player_birth_aux_point(void) { int i; int row = 3; int col = 42; int stat = 0; int stats[6]; int cost; char ch; char buf[80]; int mode = 0; /* Initialize stats */ for (i = 0; i < 6; i++) { /* Initial stats */ stats[i] = 10; } /* Roll for base hitpoints */ get_extra(); /* Roll for age/height/weight */ get_ahw(); /* Roll for social class */ get_history(); /*** Generate ***/ process_hooks(HOOK_BIRTH, "()"); /* Hack -- get a chaos patron even if you are not a chaos warrior */ p_ptr->chaos_patron = (randint(MAX_PATRON)) - 1; /* Get luck */ p_ptr->luck_base = rp_ptr->luck + rmp_ptr->luck + rand_range( -5, 5); p_ptr->luck_max = p_ptr->luck_base; /* Interact */ while (1) { /* Reset cost */ cost = 0; /* Process stats */ for (i = 0; i < 6; i++) { /* Variable stat maxes */ if (p_ptr->maximize) { /* Reset stats */ p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stats[i]; } /* Fixed stat maxes */ else { /* Obtain a "bonus" for "race" and "class" */ int bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i]; /* Apply the racial/class bonuses */ p_ptr->stat_cur[i] = p_ptr->stat_max[i] = modify_stat_value(stats[i], bonus); } /* Total cost */ cost += birth_stat_costs[stats[i] - 10]; } /* Restrict cost */ if (cost > 48) { /* Warning */ bell(); /* Reduce stat */ stats[stat]--; /* Recompute costs */ continue; } /* Gold is inversely proportional to cost */ p_ptr->au = (100 * (48 - cost)) + 100; /* Maximum of 600 gold */ if (p_ptr->au > 600) p_ptr->au = 600; /* Calculate the bonuses and hitpoints */ p_ptr->update |= (PU_BONUS | PU_HP); /* Update stuff */ update_stuff(); /* Fully healed */ p_ptr->chp = p_ptr->mhp; /* Fully rested */ p_ptr->csp = p_ptr->msp; /* Display the player */ display_player(mode); /* Display the costs header */ put_str("Cost", row - 2, col + 32); /* Display the costs */ for (i = 0; i < 6; i++) { /* Display cost */ strnfmt(buf, 80, "%4d", birth_stat_costs[stats[i] - 10]); put_str(buf, row + (i - 1), col + 32); } /* Prompt XXX XXX XXX */ strnfmt(buf, 80, "Total Cost %2d/48. Use 2/8 to move, 4/6 to modify, ESC to accept.", cost); prt(buf, 0, 0); /* Place cursor just after cost of current stat */ Term_gotoxy(col + 36, row + stat - 1); /* Get key */ ch = inkey(); /* Quit */ if (ch == 'Q') quit(NULL); /* Start over */ if (ch == 'S') return (FALSE); /* Done */ if (ch == ESCAPE) break; /* Prev stat */ if (ch == '8') { stat = (stat + 6 - 1) % 6; } /* Next stat */ if (ch == '2') { stat = (stat + 1) % 6; } /* Decrease stat */ if ((ch == '4') && (stats[stat] > 10)) { stats[stat]--; } /* Increase stat */ if ((ch == '6') && (stats[stat] < 18)) { stats[stat]++; } } /* Done */ return (TRUE); } /* * Use the autoroller or not to generate a char */ static bool_ player_birth_aux_auto() { int i, j, m, v; int mode = 0; bool_ flag = FALSE; bool_ prev = FALSE; char c; char b1 = '['; char b2 = ']'; char buf[80]; char inp[80]; #ifdef ALLOW_AUTOROLLER /* Initialize */ if (autoroll) { int mval[6]; /* Clear fields */ auto_round = 0L; last_round = 0L; /* Clean up */ clear_from(10); /* Prompt for the minimum stats */ put_str("Enter minimum attribute for: ", 15, 2); /* Output the maximum stats */ for (i = 0; i < 6; i++) { char stat_buf[15]; /* Reset the "success" counter */ stat_match[i] = 0; /* Race/Class bonus */ j = rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i]; /* Obtain the "maximal" stat */ m = adjust_stat(17, j, TRUE); /* Save the maximum */ mval[i] = m; /* Extract a textual format */ cnv_stat(m, stat_buf); strnfmt(inp, 80, "(Max of %s):", stat_buf); /* Prepare a prompt */ strnfmt(buf, 80, "%-5s: %-20s", stat_names[i], inp); /* Dump the prompt */ put_str(buf, 16 + i, 5); } /* Input the minimum stats */ for (i = 0; i < 6; i++) { /* Get a minimum stat */ while (TRUE) { char *s; /* Move the cursor */ put_str("", 16 + i, 30); /* Default */ strcpy(inp, ""); /* Get a response (or escape) */ if (!askfor_aux(inp, 8)) inp[0] = '\0'; /* Weirdos stat display .. erm .. I mean, original stat display */ if (!linear_stats) { /* Hack -- add a fake slash */ strcat(inp, "/"); /* Hack -- look for the "slash" */ s = strchr(inp, '/'); /* Hack -- Nuke the slash */ *s++ = '\0'; /* Hack -- Extract an input */ v = atoi(inp) + atoi(s); } else { int z = atoi(inp); if (z <= 18) v = z; else { int extra = z - 18; v = 18 + (extra * 10); } } /* Break on valid input */ if (v <= mval[i]) break; } /* Save the minimum stat */ stat_limit[i] = (v > 0) ? v : 0; } } #endif /* ALLOW_AUTOROLLER */ /* Roll */ while (TRUE) { /* Feedback */ if (autoroll) { Term_clear(); put_str("Name :", 2, 1); put_str("Sex :", 3, 1); put_str("Race :", 4, 1); put_str("Class:", 5, 1); c_put_str(TERM_L_BLUE, player_name, 2, 9); c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 9); strnfmt(buf, 80, "%s", get_player_race_name(p_ptr->prace, p_ptr->pracem)); c_put_str(TERM_L_BLUE, buf, 4, 9); c_put_str(TERM_L_BLUE, spp_ptr->title + c_name, 5, 9); /* Label stats */ put_str("STR:", 2 + A_STR, 61); put_str("INT:", 2 + A_INT, 61); put_str("WIS:", 2 + A_WIS, 61); put_str("DEX:", 2 + A_DEX, 61); put_str("CON:", 2 + A_CON, 61); put_str("CHR:", 2 + A_CHR, 61); /* Note when we started */ last_round = auto_round; /* Indicate the state */ put_str("(Hit ESC to abort)", 11, 61); /* Label count */ put_str("Round:", 9, 61); } /* Otherwise just get a character */ else { /* Get a new character */ get_stats(); } /* Auto-roll */ while (autoroll) { bool_ accept = TRUE; /* Get a new character */ get_stats(); /* Advance the round */ auto_round++; /* Hack -- Prevent overflow */ if (auto_round >= 1000000L) break; /* Check and count acceptable stats */ for (i = 0; i < 6; i++) { /* This stat is okay */ if (stat_use[i] >= stat_limit[i]) { stat_match[i]++; } /* This stat is not okay */ else { accept = FALSE; } } /* Break if "happy" */ if (accept) break; /* Take note every 25 rolls */ flag = (!(auto_round % AUTOROLLER_STEP)); /* Update display occasionally */ if (flag || (auto_round < last_round + 100)) { /* Dump data */ birth_put_stats(); /* Dump round */ put_str(format("%6ld", auto_round), 9, 73); /* Make sure they see everything */ Term_fresh(); #ifndef USE_FAST_AUTOROLLER /* Delay 1/10 second */ if (fast_autoroller && flag) Term_xtra(TERM_XTRA_DELAY, 100); #endif /* Do not wait for a key */ inkey_scan = TRUE; /* Check for a keypress */ if (inkey()) break; } } /* Flush input */ flush(); /*** Display ***/ /* Mode */ mode = 0; /* Roll for base hitpoints */ get_extra(); /* Roll for age/height/weight */ get_ahw(); /* Roll for social class */ get_history(); /* Roll for gold */ get_money(); /*** Generate ***/ process_hooks(HOOK_BIRTH, "()"); /* Hack -- get a chaos patron even if you are not a chaos warrior */ p_ptr->chaos_patron = (randint(MAX_PATRON)) - 1; /* Input loop */ while (TRUE) { /* Calculate the bonuses and hitpoints */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_BODY); /* Update stuff */ update_stuff(); /* Fully healed */ p_ptr->chp = p_ptr->mhp; /* Fully rested */ p_ptr->csp = p_ptr->msp; /* Display the player */ display_player(mode); /* Prepare a prompt (must squeeze everything in) */ Term_gotoxy(2, 23); Term_addch(TERM_WHITE, b1); Term_addstr( -1, TERM_WHITE, "'r' to reroll"); if (prev) Term_addstr( -1, TERM_WHITE, ", 'p' for prev"); if (mode) Term_addstr( -1, TERM_WHITE, ", 'h' for Misc."); else Term_addstr( -1, TERM_WHITE, ", 'h' for History"); Term_addstr( -1, TERM_WHITE, ", or ESC to accept"); Term_addch(TERM_WHITE, b2); /* Prompt and get a command */ c = inkey(); /* Quit */ if (c == 'Q') quit(NULL); /* Start over */ if (c == 'S') return (FALSE); /* Escape accepts the roll */ if (c == ESCAPE) break; /* Reroll this character */ if ((c == ' ') || (c == 'r')) break; /* Previous character */ if (prev && (c == 'p')) { load_prev_data(TRUE); continue; } /* Toggle the display */ if ((c == 'H') || (c == 'h')) { mode = ((mode != 0) ? 0 : 1); continue; } /* Help */ if (c == '?') { do_cmd_help(); continue; } /* Warning */ bell(); } /* Are we done? */ if (c == ESCAPE) break; /* Save this for the "previous" character */ save_prev_data(); /* Note that a previous roll exists */ prev = TRUE; } /* Clear prompt */ clear_from(23); return (TRUE); } /* * Helper function for 'player_birth()' * * The delay may be reduced, but is recommended to keep players * from continuously rolling up characters, which can be VERY * expensive CPU wise. And it cuts down on player stupidity. */ static bool_ player_birth_aux() { char c; int i, j; int y = 0, x = 0; char old_history[4][60]; /* Ask */ if (!player_birth_aux_ask()) return (FALSE); for (i = 1; i < max_s_idx; i++) s_info[i].dev = FALSE; for (i = 1; i < max_s_idx; i++) { s32b value = 0, mod = 0; compute_skills(&value, &mod, i); init_skill(value, mod, i); /* Develop only revelant branches */ if (s_info[i].value || s_info[i].mod) { int z = s_info[i].father; while (z != -1) { s_info[z].dev = TRUE; z = s_info[z].father; if (z == 0) break; } } } if (do_quick_start) { load_prev_data(FALSE); /* Roll for base hitpoints */ get_extra(); /* Calculate the bonuses and hitpoints */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_BODY); /* Update stuff */ update_stuff(); /* Fully healed */ p_ptr->chp = p_ptr->mhp; /* Fully rested */ p_ptr->csp = p_ptr->msp; } else { /* Point based */ if (point_based) { if (!player_birth_aux_point()) return FALSE; } /* Auto-roll */ else { if (!player_birth_aux_auto()) return FALSE; } /* Edit character background */ for (i = 0; i < 4; i++) { strnfmt(old_history[i], 60, "%s", history[i]); } /* Turn 0 to space */ for (i = 0; i < 4; i++) { for (j = 0; history[i][j]; j++) /* loop */; for (; j < 59; j++) history[i][j] = ' '; } display_player(1); c_put_str(TERM_L_GREEN, "(Character Background - Edit Mode)", 15, 20); while (TRUE) { for (i = 0; i < 4; i++) { put_str(history[i], i + 16, 10); } c_put_str(TERM_L_BLUE, format("%c", history[y][x]), y + 16, x + 10); /* Place cursor just after cost of current stat */ Term_gotoxy(x + 10, y + 16); c = inkey(); if (c == '8') { y--; if (y < 0) y = 3; } else if (c == '2') { y++; if (y > 3) y = 0; } else if (c == '6') { x++; if (x > 59) x = 0; } else if (c == '4') { x--; if (x < 0) x = 59; } else if (c == '\r') { break; } else if (c == ESCAPE) { for (i = 0; i < 4; i++) { strnfmt(history[i], 60, "%s", old_history[i]); put_str(history[i], i + 16, 10); } break; } else { history[y][x++] = c; if (x > 58) { x = 0; y++; if (y > 3) y = 0; } } } /*** Finish up ***/ /* Get a name, recolor it, prepare savefile */ get_name(); /* Prompt for it */ prt("['Q' to suicide, 'S' to start over, or ESC to continue]", 23, 10); /* Get a key */ c = inkey(); /* Quit */ if (c == 'Q') quit(NULL); /* Start over */ if (c == 'S') return (FALSE); } /* Save this for the next character */ previous_char.quick_ok = TRUE; save_prev_data(); /* Accept */ return (TRUE); } /* * Helper function for validate_bg(). */ static void validate_bg_aux(int chart, bool_ chart_checked[], char *buf) { char *s; int i; /* Assume the chart does not exist */ bool_ chart_exists = FALSE; /* Assume the chart is not complete */ bool_ chart_complete = FALSE; int bg_max = max_bg_idx; /* No chart */ if (!chart) return; /* Already saw this chart */ if (chart_checked[chart]) return; /* Build a debug message */ s = buf + strlen(buf); /* XXX XXX XXX */ (void) strnfmt(s, -1, "%d --> ", chart); /* Check each chart */ for (i = 0; i < bg_max; i++) { /* Require same chart */ if (bg[i].chart != chart) continue; /* The chart exists */ chart_exists = TRUE; /* Validate the "next" chart recursively */ validate_bg_aux(bg[i].next, chart_checked, buf); /* Require a terminator */ if (bg[i].roll != 100) continue; /* The chart is complete */ chart_complete = TRUE; } /* Failed: The chart does not exist */ if (!chart_exists) { quit_fmt("birth.c: bg[] chart %d does not exist\n%s", chart, buf); } /* Failed: The chart is not complete */ if (!chart_complete) { quit_fmt("birth.c: bg[] chart %d is not complete", chart); } /* Remember we saw this chart */ chart_checked[chart] = TRUE; /* Build a debug message */ *s = 0; } /* * Verify that the bg[] table is valid. */ static void validate_bg(void) { int i, race; bool_ chart_checked[512]; char buf[1024]; for (i = 0; i < 512; i++) chart_checked[i] = FALSE; /* Check each race */ for (race = 0; race < max_rp_idx; race++) { /* Get the first chart for this race */ int chart = race_info[race].chart; (void) strcpy(buf, ""); /* Validate the chart recursively */ validate_bg_aux(chart, chart_checked, buf); } } /* * Initialize a random town */ void init_town(int t_idx, int level) { town_type *t_ptr = &town_info[t_idx]; /* Mark it as existent */ t_ptr->flags |= (TOWN_REAL); /* Mark it as not found */ t_ptr->flags &= ~(TOWN_KNOWN); /* Generation seed for the town */ t_ptr->seed = randint(0x10000000); /* Total hack and not even used */ t_ptr->numstores = 8; } /* * Create a new character. * * Note that we may be called with "junk" leftover in the various * fields, so we must be sure to clear them first. */ void player_birth(void) { int i, j, rtown = TOWN_RANDOM; /* Validate the bg[] table */ validate_bg(); /* Create a new character */ while (1) { /* Wipe the player */ player_wipe(); /* Roll up a new character */ if (player_birth_aux()) break; } /* Finish skills */ p_ptr->skill_points = 0; p_ptr->skill_last_level = 1; recalc_skills(FALSE); /* grab level 1 abilities */ for (i = 0; i < max_ab_idx; i++) ab_info[i].acquired = FALSE; apply_level_abilities(1); /* Complete the god */ i = p_ptr->pgod; p_ptr->pgod = 0; follow_god(i, TRUE); /* Select the default melee type */ select_default_melee(); /* Make a note file if that option is set */ if (take_notes) { add_note_type(NOTE_BIRTH); } /* Note player birth in the message recall */ message_add(MESSAGE_MSG, " ", TERM_L_BLUE); message_add(MESSAGE_MSG, " ", TERM_L_BLUE); message_add(MESSAGE_MSG, "====================", TERM_L_BLUE); message_add(MESSAGE_MSG, " ", TERM_L_BLUE); message_add(MESSAGE_MSG, " ", TERM_L_BLUE); /* Hack -- outfit the player */ player_outfit(); /* Initialize random towns in the dungeons */ for (i = 0; i < max_d_idx; i++) { dungeon_info_type *d_ptr = &d_info[i]; int num = 0, z; d_ptr->t_num = 0; for (z = 0; z < TOWN_DUNGEON; z++) { d_ptr->t_idx[z] = 0; d_ptr->t_level[z] = 0; } if (!(d_ptr->flags1 & DF1_RANDOM_TOWNS)) continue; /* Can we add a town ? */ while (magik(TOWN_CHANCE - (num * 10))) { int lev; d_ptr->t_idx[num] = rtown; rtown++; while (TRUE) { int j; bool_ ok = TRUE; lev = rand_range(d_ptr->mindepth, d_ptr->maxdepth - 1); /* Be sure it wasnt already used */ for (j = 0; j < num; j++) { if (d_ptr->t_level[j] == lev) ok = FALSE; } /* Ok found one */ if (ok) break; } d_ptr->t_level[num] = lev; if (wizard) message_add(MESSAGE_MSG, format("Random dungeon town: d_idx:%d, lev:%d", i, lev), TERM_WHITE); /* Create the town */ init_town(d_ptr->t_idx[num], d_ptr->t_level[num]); num++; /* No free slots left */ if (num >= TOWN_DUNGEON) break; } d_ptr->t_num = num; } /* Init the towns */ for (i = 1; i < max_towns; i++) { /* Not destroyed ! yet .. ;) */ town_info[i].destroyed = FALSE; /* Ignore non-existent towns */ if (!(town_info[i].flags & (TOWN_REAL))) continue; create_stores_stock(i); /* Init the stores */ for (j = 0; j < max_st_idx; j++) { /* Initialize */ store_init(i, j); } } /* Init wilderness seeds */ for (i = 0; i < max_wild_x; i++) { for (j = 0; j < max_wild_y; j++) { wild_map[j][i].seed = rand_int(0x10000000); wild_map[j][i].entrance = 0; wild_map[j][i].known = FALSE; } } /* Select bounty monsters. */ select_bounties(); } char savefile_module[46][80]; char savefile_names[46][30]; char savefile_desc[46][80]; bool_ savefile_alive[46]; int savefile_idx[46]; /* * Grab all the names from an index */ int load_savefile_names() { FILE *fff; char buf[1024]; char tmp[50]; char player_base_save[32]; int max = 0, fd; /* Build the filename */ strcpy(tmp, "global.svg"); path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Read the file */ fff = my_fopen(buf, "r"); /* Failure */ if (!fff) return (0); /* Save the current 'player_base' */ strncpy(player_base_save, player_base, 32); /* * Parse, use '@' intead of ':' as a separator because it cannot exists * in savefiles */ while (0 == my_fgets(fff, buf, 1024)) { int i = 0, start, count; /* Check for pre-ToME 2.1.2 file */ count = 0; i = 0; while (buf[i] && buf[i] != '\n') { if (buf[i] == '@') ++count; ++i; } /* Check module if a current svg file */ start = 0; i = 0; if (count > 1) { while (buf[i] != '@') { savefile_module[max][i - start] = buf[i]; i++; } savefile_module[max][i] = '\0'; i++; } /* Default to ToME for old files */ else { savefile_module[max][0] = 'T'; savefile_module[max][1] = 'o'; savefile_module[max][2] = 'M'; savefile_module[max][3] = 'E'; savefile_module[max][4] = '\0'; } if (buf[i] == '0') savefile_alive[max] = FALSE; else if (buf[i] == '1') savefile_alive[max] = TRUE; i++; start = i; while (buf[i] != '@') { savefile_names[max][i - start] = buf[i]; i++; } savefile_names[max][i - start] = '\0'; i++; strcpy(savefile_desc[max], buf + i); /* Build platform-dependent savefile name */ strncpy(player_base, savefile_names[max], 32); process_player_name(TRUE); /* File type is 'SAVE' */ FILE_TYPE(FILE_TYPE_SAVE); /* Try to open the savefile */ fd = fd_open(savefile, O_RDONLY); /* Still existing ? */ if (fd >= 0) { fd_close(fd); max++; } } my_fclose(fff); /* Restore the values of 'player_base' and 'savefile' */ strncpy(player_base, player_base_save, 32); process_player_name(TRUE); return (max); } /* * Save all the names from an index */ void save_savefile_names() { FILE *fff; char buf[1024]; char tmp[50]; int max = load_savefile_names(), i; /* Build the filename */ strcpy(tmp, "global.svg"); path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp); /* File type is "TEXT" */ FILE_TYPE(FILE_TYPE_TEXT); /* Read the file */ fff = my_fopen(buf, "w"); /* Failure */ if (!fff) return; /* * Save, use '@' intead of ':' as a separator because it cannot exists * in savefiles */ fprintf(fff, "%s@%c%s@%s, the %s %s is %s\n", game_module, (death) ? '0' : '1', player_base, player_name, get_player_race_name(p_ptr->prace, p_ptr->pracem), spp_ptr->title + c_name, (!death) ? "alive" : "dead"); for (i = 0; i < max; i++) { if (!strcmp(savefile_names[i], player_base)) continue; fprintf(fff, "%s@%c%s@%s\n", savefile_module[i], (savefile_alive[i]) ? '1' : '0', savefile_names[i], savefile_desc[i]); } my_fclose(fff); } static void dump_savefiles(int sel, int max) { int i; char buf[40], pre = ' ', post = ')'; char ind; for (i = 0; i < max; i++) { ind = I2A(i % 26); if (i >= 26) ind = toupper(ind); if (sel == i) { pre = '['; post = ']'; } else { pre = ' '; post = ')'; } if (i == 0) strnfmt(buf, 40, "%c%c%c New Character", pre, ind, post); else if (i == 1) strnfmt(buf, 40, "%c%c%c Load Savefile", pre, ind, post); else strnfmt(buf, 40, "%c%c%c %s", pre, ind, post, savefile_names[savefile_idx[i - 2]]); if (sel == i) { if (i >= 2) { if (savefile_alive[i - 2]) c_put_str(TERM_L_GREEN, savefile_desc[savefile_idx[i - 2]], 5, 0); else c_put_str(TERM_L_RED, savefile_desc[savefile_idx[i - 2]], 5, 0); } else if (i == 1) c_put_str(TERM_YELLOW, "Load an existing savefile that is not in the list", 5, 0); else c_put_str(TERM_YELLOW, "Create a new character", 5, 0); c_put_str(TERM_L_BLUE, buf, 6 + (i / 4), 20 * (i % 4)); } else put_str(buf, 6 + (i / 4), 20 * (i % 4)); } } /* Asks for new game or load game */ bool_ no_begin_screen = FALSE; bool_ begin_screen() { int m, k, sel, max; savefile_try_again: sel = 0; /* Grab the savefiles */ max = load_savefile_names(); /* Get only the usable savefiles */ for (k = 0, m = 0; k < max; k++) { s32b can_use; call_lua("module_savefile_loadable", "(s)", "d", savefile_module[k], &can_use); if (can_use) { savefile_idx[m++] = k; } } max = m + 2; if (max > 2) sel = 2; while (TRUE) { /* Clear screen */ Term_clear(); /* Let the user choose */ c_put_str(TERM_YELLOW, format("Welcome to %s! To play you will need a character.", game_module), 1, 10); put_str("Press 8/2/4/6 to move, Return to select, Backspace to delete a savefile.", 3, 3); put_str("and Esc to quit.", 4, 32); dump_savefiles(sel, max); k = inkey(); if (k == ESCAPE) { quit(NULL); } if (k == '6') { sel++; if (sel >= max) sel = 0; continue; } else if (k == '4') { sel--; if (sel < 0) sel = max - 1; continue; } else if (k == '2') { sel += 4; if (sel >= max) sel = sel % max; continue; } else if (k == '8') { sel -= 4; if (sel < 0) sel = (sel + max - 1) % max; continue; } else if (k == '\r') { if (sel < 26) k = I2A(sel); else k = toupper(I2A(sel)); } else if (((k == 0x7F) || (k == '\010')) && (sel >= 2)) { char player_base_save[32]; if (!get_check(format("Really delete '%s'?", savefile_names[savefile_idx[sel - 2]]))) continue; /* Save current 'player_base' */ strncpy(player_base_save, player_base, 32); /* Build platform-dependent save file name */ strncpy(player_base, savefile_names[savefile_idx[sel - 2]], 32); process_player_name(TRUE); /* Remove the savefile */ fd_kill(savefile); /* Restore 'player_base' and 'savefile' */ strncpy(player_base, player_base_save, 32); process_player_name(TRUE); /* Reload, gods I hate using goto .. */ goto savefile_try_again; continue; } if (k == 'a') { /* Display prompt */ prt("Enter the name of the savefile that will hold this character: ", 23, 0); /* Ask the user for a string */ if (!askfor_aux(player_base, 15)) continue; /* Process the player name */ process_player_name(TRUE); return (TRUE); } if (k == 'b') { /* Display prompt */ prt("Enter the name of a savefile: ", 23, 0); /* Ask the user for a string */ if (!askfor_aux(player_base, 15)) continue; /* Process the player name */ process_player_name(TRUE); return (FALSE); } else { int x; if (islower(k)) x = A2I(k); else x = A2I(tolower(k)) + 26; if ((x < 2) || (x >= max)) continue; strnfmt(player_base, 32, "%s", savefile_names[savefile_idx[x - 2]]); /* Process the player name */ process_player_name(TRUE); return (FALSE); } } /* Shouldnt happen */ return (FALSE); }