/*
* File: newgame.cc
* Summary: Functions used when starting a new game.
* Written by: Linley Henzell
*/
#include "AppHdr.h"
#include "newgame.h"
#include "ng-init.h"
#include "ng-input.h"
#include "ng-restr.h"
#include "jobs.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <algorithm>
#ifdef UNIX
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#include "externs.h"
#include "options.h"
#include "species.h"
#include "abl-show.h"
#include "artefact.h"
#include "cio.h"
#include "command.h"
#include "describe.h"
#include "dungeon.h"
#include "files.h"
#include "food.h"
#include "initfile.h"
#include "itemname.h"
#include "itemprop.h"
#include "items.h"
#include "macro.h"
#include "makeitem.h"
#include "menu.h"
#include "mutation.h"
#include "misc.h"
#include "player.h"
#include "random.h"
#include "religion.h"
#include "skills.h"
#include "skills2.h"
#include "spl-book.h"
#include "spl-util.h"
#include "state.h"
#include "stuff.h"
#include "tutorial.h"
#define MIN_START_STAT 3
static void _give_basic_knowledge(job_type which_job);
static void _give_basic_spells(job_type which_job);
static void _give_last_paycheck(job_type which_job);
static void _init_player(void);
static void _jobs_stat_init(job_type which_job);
static void _species_stat_init(species_type which_species);
static void _create_wanderer(void);
static bool _give_items_skills(void);
////////////////////////////////////////////////////////////////////////
// Remember player's startup options
//
static newgame_def ng;
// XXX: temporary to get data in and out of
// purely rewritten functions.
void newgame_def::init(const player &p)
{
name = p.your_name;
species = p.species;
job = p.char_class;
}
void newgame_def::save(player &p)
{
p.your_name = name;
p.species = species;
p.char_class = job;
}
static char ng_race, ng_cls;
static bool ng_random;
static int ng_ck, ng_dk;
static int ng_weapon;
static int ng_book;
static int ng_wand;
static god_type ng_pr;
static void _reset_newgame_options(void)
{
ng_race = ng_cls = 0;
ng_random = false;
ng_ck = GOD_NO_GOD;
ng_dk = DK_NO_SELECTION;
ng_pr = GOD_NO_GOD;
ng_weapon = WPN_UNKNOWN;
ng_book = SBT_NO_SELECTION;
ng_wand = SWT_NO_SELECTION;
}
static void _save_newgame_options(void)
{
// Note that we store race and class directly here, whereas up until
// now we've been storing the hotkey.
Options.prev_race = ng_race;
Options.prev_cls = ng_cls;
Options.prev_randpick = ng_random;
Options.prev_ck = ng_ck;
Options.prev_dk = ng_dk;
Options.prev_pr = ng_pr;
Options.prev_weapon = ng_weapon;
Options.prev_book = ng_book;
Options.prev_wand = ng_wand;
write_newgame_options_file();
}
static void _set_startup_options(void)
{
Options.race = Options.prev_race;
Options.cls = Options.prev_cls;
Options.chaos_knight = Options.prev_ck;
Options.death_knight = Options.prev_dk;
Options.priest = Options.prev_pr;
Options.weapon = Options.prev_weapon;
Options.book = Options.prev_book;
Options.wand = Options.prev_wand;
}
static bool _prev_startup_options_set(void)
{
// Are these two enough? They should be, in theory, since we'll
// remember the player's weapon and god choices.
return (Options.prev_race && Options.prev_cls);
}
static std::string _get_opt_race_name(char race)
{
species_type prace = get_species(letter_to_index(race));
return (prace == SP_UNKNOWN ? "Random" : species_name(prace, 1));
}
static std::string _get_opt_class_name(char oclass)
{
job_type pclass = get_class(letter_to_index(oclass));
return (pclass == JOB_UNKNOWN ? "Random" : get_class_name(pclass));
}
static std::string _prev_startup_description(void)
{
if (Options.prev_race == '*' && Options.prev_cls == '*')
Options.prev_randpick = true;
if (Options.prev_randpick)
return "Random character";
if (Options.prev_cls == '?')
return "Random " + _get_opt_race_name(Options.prev_race);
return _get_opt_race_name(Options.prev_race) + " " +
_get_opt_class_name(Options.prev_cls);
}
// Output full character info when weapons/books/religion are chosen.
static void _print_character_info()
{
clrscr();
// At this point all of name, species and class should be decided.
if (!you.your_name.empty()
&& you.char_class != JOB_UNKNOWN && you.species != SP_UNKNOWN)
{
cprintf("Welcome, ");
textcolor( YELLOW );
cprintf("%s the %s %s." EOL, you.your_name.c_str(), species_name(you.species, 1).c_str(),
get_class_name(you.char_class));
}
}
int give_first_conjuration_book()
{
// Assume the fire/earth book, as conjurations is strong with fire. -- bwr
int book = BOOK_CONJURATIONS_I;
// Conjuration books are largely Fire or Ice, so we'll use
// that as the primary condition, and air/earth to break ties. -- bwr
if (you.skills[SK_ICE_MAGIC] > you.skills[SK_FIRE_MAGIC]
|| you.skills[SK_FIRE_MAGIC] == you.skills[SK_ICE_MAGIC]
&& you.skills[SK_AIR_MAGIC] > you.skills[SK_EARTH_MAGIC])
{
book = BOOK_CONJURATIONS_II;
}
else if (you.skills[SK_FIRE_MAGIC] == 0 && you.skills[SK_EARTH_MAGIC] == 0)
{
// If we're here it's because we were going to default to the
// fire/earth book... but we don't have those skills. So we
// choose randomly based on the species weighting, again
// ignoring air/earth which are secondary in these books. - bwr
if (random2(species_skills(SK_ICE_MAGIC, you.species)) <
random2(species_skills(SK_FIRE_MAGIC, you.species)))
{
book = BOOK_CONJURATIONS_II;
}
}
return (book);
}
// Determines if a species is valid. If 'display' is true, returns if
// the species is displayable in the new game screen - this is
// primarily used to suppress the display of the draconian variants.
static bool _is_species_valid_choice(species_type species, bool display = true)
{
if (species < 0 || species > NUM_SPECIES)
return (false);
if (species >= SP_ELF) // These are all invalid.
return (false);
// No problem with these.
if (species <= SP_RED_DRACONIAN || species > SP_BASE_DRACONIAN)
return (true);
// Draconians other than red return false if display == true.
return (!display);
}
static void _pick_random_species_and_class( bool unrestricted_only )
{
// We pick both species and class at the same time to give each
// valid possibility a fair chance. For proof that this will
// work correctly see the proof in religion.cc:handle_god_time().
int job_count = 0;
species_type species = SP_UNKNOWN;
job_type job = JOB_UNKNOWN;
// For each valid (species, class) choose one randomly.
for (int sp = 0; sp < NUM_SPECIES; sp++)
{
// We only want draconians counted once in this loop...
// We'll add the variety lower down -- bwr
if (!_is_species_valid_choice(static_cast<species_type>(sp)))
continue;
for (int cl = JOB_FIGHTER; cl < NUM_JOBS; cl++)
{
if (is_good_combination(static_cast<species_type>(sp),
static_cast<job_type>(cl),
unrestricted_only))
{
job_count++;
if (one_chance_in(job_count))
{
species = static_cast<species_type>(sp);
job = static_cast<job_type>(cl);
}
}
}
}
// At least one job must exist in the game, else we're in big trouble.
ASSERT(species != SP_UNKNOWN && job != JOB_UNKNOWN);
// Return draconian variety here.
if (species == SP_RED_DRACONIAN)
you.species = random_draconian_player_species();
else
you.species = species;
you.char_class = job;
}
static bool _check_saved_game(void)
{
FILE *handle;
std::string basename = get_savedir_filename(you.your_name, "", "");
std::string savename = basename + ".sav";
#ifdef LOAD_UNPACKAGE_CMD
std::string zipname = basename + PACKAGE_SUFFIX;
handle = fopen(zipname.c_str(), "rb+");
if (handle != NULL)
{
fclose(handle);
cprintf(EOL "Loading game..." EOL);
// Create command.
char cmd_buff[1024];
std::string directory = get_savedir();
escape_path_spaces(basename);
escape_path_spaces(directory);
snprintf( cmd_buff, sizeof(cmd_buff), LOAD_UNPACKAGE_CMD,
basename.c_str(), directory.c_str() );
if (system( cmd_buff ) != 0)
{
cprintf( EOL "Warning: Zip command (LOAD_UNPACKAGE_CMD) "
"returned non-zero value!" EOL );
}
// Remove save game package.
unlink(zipname.c_str());
}
#endif
handle = fopen(savename.c_str(), "rb+");
if (handle != NULL)
{
fclose(handle);
return (true);
}
return (false);
}
static void _give_starting_food()
{
// These undead start with no food.
if (you.species == SP_MUMMY || you.species == SP_GHOUL)
return;
item_def item;
item.quantity = 1;
if (you.species == SP_SPRIGGAN)
{
item.base_type = OBJ_POTIONS;
item.sub_type = POT_PORRIDGE;
}
else if (you.species == SP_VAMPIRE)
{
item.base_type = OBJ_POTIONS;
item.sub_type = POT_BLOOD;
init_stack_blood_potions(item);
}
else
{
item.base_type = OBJ_FOOD;
if (you.species == SP_HILL_ORC || you.species == SP_KOBOLD
|| player_genus(GENPC_OGRE) || you.species == SP_TROLL)
{
item.sub_type = FOOD_MEAT_RATION;
}
else
item.sub_type = FOOD_BREAD_RATION;
}
// Give another one for hungry species.
if (player_mutation_level(MUT_FAST_METABOLISM))
item.quantity = 2;
const int slot = find_free_slot(item);
you.inv[slot] = item; // will ASSERT if couldn't find free slot
}
static void _mark_starting_books()
{
for (int i = 0; i < ENDOFPACK; ++i)
if (you.inv[i].is_valid() && you.inv[i].base_type == OBJ_BOOKS)
mark_had_book(you.inv[i]);
}
static void _racialise_starting_equipment()
{
for (int i = 0; i < ENDOFPACK; ++i)
{
if (you.inv[i].is_valid())
{
// Don't change object type modifier unless it starts plain.
if ((you.inv[i].base_type == OBJ_ARMOUR
|| you.inv[i].base_type == OBJ_WEAPONS
|| you.inv[i].base_type == OBJ_MISSILES)
&& get_equip_race(you.inv[i]) == ISFLAG_NO_RACE)
{
// Now add appropriate species type mod.
// Fighters don't get elven body armour.
if (player_genus(GENPC_ELVEN)
&& (you.char_class != JOB_FIGHTER
|| you.inv[i].base_type != OBJ_ARMOUR
|| get_armour_slot(you.inv[i]) != EQ_BODY_ARMOUR))
{
set_equip_race(you.inv[i], ISFLAG_ELVEN);
}
else if (player_genus(GENPC_DWARVEN))
set_equip_race(you.inv[i], ISFLAG_DWARVEN);
else if (you.species == SP_HILL_ORC)
set_equip_race(you.inv[i], ISFLAG_ORCISH);
}
}
}
}
// Characters are actually granted skill points, not skill levels.
// Here we take racial aptitudes into account in determining final
// skill levels.
static void _reassess_starting_skills()
{
for (int i = 0; i < NUM_SKILLS; ++i)
{
if (you.skills[i] == 0
&& (you.species != SP_VAMPIRE || i != SK_UNARMED_COMBAT))
{
continue;
}
// Grant the amount of skill points required for a human.
const int points = skill_exp_needed(you.skills[i]);
you.skill_points[i] = (points * species_skills(i, SP_HUMAN)) / 100 + 1;
// Find out what level that earns this character.
const int sp_diff = species_skills(i, you.species);
you.skills[i] = 0;
for (int lvl = 1; lvl <= 8; ++lvl)
{
if (you.skill_points[i] > (skill_exp_needed(lvl) * sp_diff) / 100)
you.skills[i] = lvl;
else
break;
}
// Vampires should always have Unarmed Combat skill.
if (you.species == SP_VAMPIRE && i == SK_UNARMED_COMBAT
&& you.skills[i] < 1)
{
you.skill_points[i] = (skill_exp_needed(1) * sp_diff) / 100;
you.skills[i] = 1;
}
// Wanderers get at least 1 level in their skills.
if (you.char_class == JOB_WANDERER && you.skills[i] < 1)
{
you.skill_points[i] = (skill_exp_needed(1) * sp_diff) / 100;
you.skills[i] = 1;
}
// Spellcasters should always have Spellcasting skill.
if (i == SK_SPELLCASTING && you.skills[i] < 1)
{
you.skill_points[i] = (skill_exp_needed(1) * sp_diff) / 100;
you.skills[i] = 1;
}
}
}
// Make sure no stats are unacceptably low
// (currently possible only for GhBe -- 1KB)
static void _unfocus_stats()
{
int needed;
if ((needed = MIN_START_STAT - you.strength) > 0)
{
if (you.intel > you.dex)
you.intel -= needed;
else
you.dex -= needed;
you.strength = MIN_START_STAT;
}
if ((needed = MIN_START_STAT - you.intel) > 0)
{
if (you.strength > you.dex)
you.strength -= needed;
else
you.dex -= needed;
you.intel = MIN_START_STAT;
}
if ((needed = MIN_START_STAT - you.dex) > 0)
{
if (you.strength > you.intel)
you.strength -= needed;
else
you.intel -= needed;
you.dex = MIN_START_STAT;
}
}
// Randomly boost stats a number of times.
static void _wanderer_assign_remaining_stats(int points_left)
{
while (points_left > 0)
{
// Stats that are already high will be chosen half as often.
switch (random2(NUM_STATS))
{
case STAT_STRENGTH:
if (you.strength > 17 && coinflip())
continue;
you.strength++;
break;
case STAT_DEXTERITY:
if (you.dex > 17 && coinflip())
continue;
you.dex++;
break;
case STAT_INTELLIGENCE:
if (you.intel > 17 && coinflip())
continue;
you.intel++;
break;
}
points_left--;
}
}
static void _give_species_bonus_hp()
{
if (player_genus(GENPC_DRACONIAN) || player_genus(GENPC_DWARVEN))
inc_max_hp(1);
else
{
switch (you.species)
{
case SP_CENTAUR:
case SP_OGRE:
case SP_TROLL:
inc_max_hp(3);
break;
case SP_GHOUL:
case SP_MINOTAUR:
case SP_NAGA:
case SP_DEMIGOD:
inc_max_hp(2);
break;
case SP_HILL_ORC:
case SP_MUMMY:
case SP_MERFOLK:
inc_max_hp(1);
break;
case SP_HIGH_ELF:
case SP_VAMPIRE:
dec_max_hp(1);
break;
case SP_DEEP_ELF:
case SP_HALFLING:
case SP_KENKU:
case SP_KOBOLD:
case SP_SPRIGGAN:
dec_max_hp(2);
break;
default:
break;
}
}
}
// Adjust max_magic_points by species. {dlb}
static void _give_species_bonus_mp()
{
switch (you.species)
{
case SP_VAMPIRE:
case SP_SPRIGGAN:
case SP_DEMIGOD:
case SP_DEEP_ELF:
inc_max_mp(1);
break;
default:
break;
}
}
#ifdef DEBUG
static bool _species_is_undead( const species_type speci )
{
return (speci == SP_MUMMY || speci == SP_GHOUL || speci == SP_VAMPIRE);
}
#endif
undead_state_type get_undead_state(const species_type sp)
{
switch (sp)
{
case SP_MUMMY:
return US_UNDEAD;
case SP_GHOUL:
return US_HUNGRY_DEAD;
case SP_VAMPIRE:
return US_SEMI_UNDEAD;
default:
ASSERT(!_species_is_undead(sp));
return (US_ALIVE);
}
}
// For items that get a random colour, give them a more thematic one.
static void _apply_job_colour(item_def &item)
{
if (!Options.classic_item_colours)
return;
if (item.base_type != OBJ_ARMOUR)
return;
switch (item.sub_type)
{
case ARM_CLOAK:
case ARM_ROBE:
case ARM_NAGA_BARDING:
case ARM_CENTAUR_BARDING:
case ARM_CAP:
case ARM_WIZARD_HAT:
break;
default:
return;
}
switch (you.char_class)
{
case JOB_THIEF:
case JOB_NECROMANCER:
case JOB_ASSASSIN:
item.colour = DARKGREY;
break;
case JOB_FIRE_ELEMENTALIST:
item.colour = RED;
break;
case JOB_ICE_ELEMENTALIST:
item.colour = BLUE;
break;
case JOB_AIR_ELEMENTALIST:
item.colour = LIGHTBLUE;
break;
case JOB_EARTH_ELEMENTALIST:
item.colour = BROWN;
break;
case JOB_VENOM_MAGE:
item.colour = MAGENTA;
break;
default:
break;
}
}
bool new_game(void)
{
clrscr();
_init_player();
if (!crawl_state.startup_errors.empty()
&& !Options.suppress_startup_errors)
{
crawl_state.show_startup_errors();
clrscr();
}
if (!Options.player_name.empty())
you.your_name = Options.player_name;
textcolor(LIGHTGREY);
// Copy name into you.your_name if set from environment --
// note that you.your_name could already be set from init.txt.
// This, clearly, will overwrite such information. {dlb}
if (!SysEnv.crawl_name.empty())
you.your_name = SysEnv.crawl_name;
opening_screen();
ng.init(you);
enter_player_name(ng, true);
ng.save(you);
if (!you.your_name.empty())
{
if (_check_saved_game())
{
save_player_name();
return (false);
}
}
game_start:
_reset_newgame_options();
if (Options.random_pick)
{
_pick_random_species_and_class(Options.good_random);
ng_random = true;
}
else
{
if (Options.race != 0 && Options.cls != 0
&& Options.race != '*' && Options.cls != '*'
&& !class_allowed(get_species(letter_to_index(Options.race)),
get_class(letter_to_index(Options.cls))))
{
end(1, false,
"Incompatible species and job specified in options file.");
}
// Repeat until valid race/class combination found.
while (choose_race() && !choose_class());
}
// Pick random draconian type.
if (you.species == SP_RED_DRACONIAN)
you.species = random_draconian_player_species();
strcpy( you.class_name, get_class_name(you.char_class) );
if (Options.random_pick)
{
// For completely random combinations (!, #, or Options.random_pick)
// reroll characters until the player accepts one of them or quits.
clrscr();
std::string specs = species_name(you.species, you.experience_level);
if (specs.length() > 79)
specs = specs.substr(0, 79);
cprintf( "You are a%s %s %s." EOL,
(is_vowel( specs[0] )) ? "n" : "", specs.c_str(),
you.class_name );
cprintf(EOL "Do you want to play this combination? (ynq) [y]");
char c = getch();
if (c == ESCAPE || tolower(c) == 'q')
end(0);
if (tolower(c) == 'n')
goto game_start;
}
// New: pick name _after_ race and class choices.
if (you.your_name.empty())
{
clrscr();
std::string specs = species_name(you.species, you.experience_level);
if (specs.length() > 79)
specs = specs.substr(0, 79);
cprintf( "You are a%s %s %s." EOL,
(is_vowel( specs[0] )) ? "n" : "", specs.c_str(),
you.class_name );
ng.init(you);
enter_player_name(ng, false);
ng.save(you);
if (_check_saved_game())
{
cprintf(EOL "Do you really want to overwrite your old game? ");
char c = getch();
if (c != 'Y' && c != 'y')
{
textcolor( BROWN );
cprintf(EOL EOL "Welcome back, ");
textcolor( YELLOW );
cprintf("%s!", you.your_name.c_str());
textcolor( LIGHTGREY );
return (false);
}
}
}
// ************ Round-out character statistics and such. ************
_species_stat_init( you.species ); // must be down here {dlb}
you.is_undead = get_undead_state(you.species);
// Before we get into the inventory init, set light radius based
// on species vision. Currently, all species see out to 8 squares.
you.normal_vision = LOS_RADIUS;
you.current_vision = LOS_RADIUS;
_jobs_stat_init( you.char_class );
_give_last_paycheck( you.char_class );
_unfocus_stats();
// Needs to be done before handing out food.
give_basic_mutations(you.species);
// This function depends on stats and mutations being finalised.
// Returns false if Backspace on god/weapon/... selection.
if (!_give_items_skills())
{
// Now choose again, name stays same.
const std::string old_name = you.your_name;
Options.prev_randpick = false;
Options.prev_race = ng_race;
Options.prev_cls = ng_cls;
Options.prev_weapon = ng_weapon;
// ck, dk, pr and book are asked last --> don't need to be changed
// Reset stats.
_init_player();
Options.reset_startup_options();
you.your_name = old_name;
// Choose new character.
goto game_start;
}
_give_species_bonus_hp();
_give_species_bonus_mp();
if (you.species == SP_DEMONSPAWN)
roll_demonspawn_mutations();
// XXX: These need to be set above using functions!!! {dlb}
you.max_dex = you.dex;
you.max_strength = you.strength;
you.max_intel = you.intel;
_give_starting_food();
_mark_starting_books();
_racialise_starting_equipment();
_give_basic_spells(you.char_class);
_give_basic_knowledge(you.char_class);
initialise_item_descriptions();
_reassess_starting_skills();
calc_total_skill_points();
for (int i = 0; i < ENDOFPACK; ++i)
if (you.inv[i].is_valid())
{
// XXX: Why is this here? Elsewhere it's only ever used for runes.
you.inv[i].flags |= ISFLAG_BEEN_IN_INV;
// Identify all items in pack.
set_ident_type(you.inv[i], ID_KNOWN_TYPE);
set_ident_flags(you.inv[i], ISFLAG_IDENT_MASK);
// link properly
you.inv[i].pos.set(-1, -1);
you.inv[i].link = i;
you.inv[i].slot = index_to_letter(you.inv[i].link);
item_colour(you.inv[i]); // set correct special and colour
_apply_job_colour(you.inv[i]);
}
// Apply autoinscribe rules to inventory.
request_autoinscribe();
autoinscribe();
// Brand items as original equipment.
origin_set_inventory(origin_set_startequip);
// We calculate hp and mp here; all relevant factors should be
// finalised by now. (GDL)
calc_hp();
calc_mp();
// Make sure the starting player is fully charged up.
set_hp( you.hp_max, false );
set_mp( you.max_magic_points, false );
// tmpfile purging removed in favour of marking
Generated_Levels.clear();
initialise_branch_depths();
initialise_temples();
init_level_connectivity();
// Generate the second name of Jiyva
fix_up_jiyva_name();
_save_newgame_options();
// Pretend that a savefile was just loaded, in order to
// get things setup properly.
SavefileCallback::post_restore();
return (true);
}
static startup_book_type _book_to_start(int book)
{
switch (book)
{
case BOOK_MINOR_MAGIC_I:
case BOOK_CONJURATIONS_I:
return (SBT_FIRE);
case BOOK_MINOR_MAGIC_II:
case BOOK_CONJURATIONS_II:
return (SBT_COLD);
case BOOK_MINOR_MAGIC_III:
return (SBT_SUMM);
default:
return (SBT_NO_SELECTION);
}
}
static int _start_to_book(int firstbook, int booktype)
{
switch (firstbook)
{
case BOOK_MINOR_MAGIC_I:
switch (booktype)
{
case SBT_FIRE:
return (BOOK_MINOR_MAGIC_I);
case SBT_COLD:
return (BOOK_MINOR_MAGIC_II);
case SBT_SUMM:
return (BOOK_MINOR_MAGIC_III);
default:
return (-1);
}
case BOOK_CONJURATIONS_I:
switch (booktype)
{
case SBT_FIRE:
return (BOOK_CONJURATIONS_I);
case SBT_COLD:
return (BOOK_CONJURATIONS_II);
default:
return (-1);
}
default:
return (-1);
}
}
static bool _choose_book( int slot, int firstbook, int numbooks )
{
clrscr();
ng.init(you); // XXX
item_def &book(you.inv[slot]);
book.base_type = OBJ_BOOKS;
book.sub_type = firstbook;
book.quantity = 1;
book.plus = 0;
book.plus2 = 0;
book.special = 1;
// Assume a choice of no more than three books, and that all book
// choices are contiguous.
ASSERT(numbooks >= 1 && numbooks <= 3);
char_choice_restriction book_restrictions[3];
for (int i = 0; i < numbooks; i++)
{
book_restrictions[i] = book_restriction(
_book_to_start(firstbook + i), ng);
}
if (Options.book)
{
const int opt_book = _start_to_book(firstbook, Options.book);
if (opt_book != -1)
{
book.sub_type = opt_book;
ng_book = Options.book;
return (true);
}
}
if (Options.prev_book)
{
if (_start_to_book(firstbook, Options.prev_book) == -1
&& Options.prev_book != SBT_RANDOM)
{
Options.prev_book = SBT_NO_SELECTION;
}
}
int keyin = 0;
if (!Options.random_pick && Options.book != SBT_RANDOM)
{
_print_character_info();
textcolor( CYAN );
cprintf(EOL "You have a choice of books: "
"(Press %% for a list of aptitudes)" EOL);
for (int i = 0; i < numbooks; ++i)
{
book.sub_type = firstbook + i;
if (book_restrictions[i] == CC_UNRESTRICTED)
textcolor(LIGHTGREY);
else
textcolor(DARKGREY);
cprintf("%c - %s" EOL, 'a' + i,
book.name(DESC_PLAIN, false, true).c_str());
}
textcolor(BROWN);
cprintf(EOL "* - Random choice; + - Good random choice; "
"Bksp - Back to species and class selection; "
"X - Quit" EOL);
if (Options.prev_book != SBT_NO_SELECTION)
{
cprintf("; Enter - %s",
Options.prev_book == SBT_FIRE ? "Fire" :
Options.prev_book == SBT_COLD ? "Cold" :
Options.prev_book == SBT_SUMM ? "Summoning" :
Options.prev_book == SBT_RANDOM ? "Random"
: "Buggy Book");
}
cprintf(EOL);
do
{
textcolor( CYAN );
cprintf(EOL "Which book? ");
textcolor( LIGHTGREY );
keyin = c_getch();
switch (keyin)
{
case 'X':
cprintf(EOL "Goodbye!");
end(0);
break;
case CK_BKSP:
case ESCAPE:
case ' ':
return (false);
case '\r':
case '\n':
if (Options.prev_book != SBT_NO_SELECTION)
{
if (Options.prev_book == SBT_RANDOM)
keyin = '*';
else
{
keyin = 'a'
+ _start_to_book(firstbook, Options.prev_book)
- firstbook;
}
}
break;
case '%':
list_commands('%');
return _choose_book(slot, firstbook, numbooks);
default:
break;
}
}
while (keyin != '*' && keyin != '+'
&& (keyin < 'a' || keyin >= ('a' + numbooks)));
}
if (Options.random_pick || Options.book == SBT_RANDOM || keyin == '*'
|| keyin == '+')
{
ng_book = SBT_RANDOM;
int good_choices = 0;
if (keyin == '+'
|| Options.good_random
&& (Options.random_pick || Options.book == SBT_RANDOM))
{
for (int i = 0; i < numbooks; i++)
{
if (book_restriction(_book_to_start(firstbook + i), ng)
== CC_UNRESTRICTED
&& one_chance_in(++good_choices))
{
keyin = i;
}
}
}
if (!good_choices)
keyin = random2(numbooks);
keyin += 'a';
}
else
ng_book = _book_to_start(keyin - 'a' + firstbook);
book.sub_type = firstbook + keyin - 'a';
return (true);
}
static bool _choose_weapon()
{
weapon_type startwep[5] = { WPN_SHORT_SWORD, WPN_MACE,
WPN_HAND_AXE, WPN_SPEAR, WPN_UNKNOWN };
ng.init(you);
// Gladiators that are at least medium sized get to choose a trident
// rather than a spear.
if (you.char_class == JOB_GLADIATOR
&& you.body_size(PSIZE_BODY) >= SIZE_MEDIUM)
{
startwep[3] = WPN_TRIDENT;
}
const bool claws_allowed =
(you.char_class != JOB_GLADIATOR && you.has_claws());
if (claws_allowed)
{
for (int i = 3; i >= 0; --i)
startwep[i + 1] = startwep[i];
startwep[0] = WPN_UNARMED;
}
else
{
switch (you.species)
{
case SP_OGRE:
startwep[1] = WPN_ANKUS;
break;
case SP_MERFOLK:
startwep[3] = WPN_TRIDENT;
break;
default:
break;
}
}
char_choice_restriction startwep_restrictions[5];
const int num_choices = (claws_allowed ? 5 : 4);
for (int i = 0; i < num_choices; i++)
startwep_restrictions[i] = weapon_restriction(startwep[i], ng);
if (Options.weapon == WPN_UNARMED && claws_allowed)
{
you.inv[0].quantity = 0; // no weapon
ng_weapon = Options.weapon;
return (true);
}
if (Options.weapon != WPN_UNKNOWN && Options.weapon != WPN_RANDOM
&& Options.weapon != WPN_UNARMED)
{
// If Options.weapon is available, then use it.
for (int i = 0; i < num_choices; i++)
{
if (startwep[i] == Options.weapon
&& startwep_restrictions[i] != CC_BANNED)
{
you.inv[0].sub_type = Options.weapon;
ng_weapon = Options.weapon;
return (true);
}
}
}
int keyin = 0;
if (!Options.random_pick && Options.weapon != WPN_RANDOM)
{
_print_character_info();
textcolor( CYAN );
cprintf(EOL "You have a choice of weapons: "
"(Press %% for a list of aptitudes)" EOL);
bool prevmatch = false;
for (int i = 0; i < num_choices; i++)
{
ASSERT(startwep[i] != WPN_UNKNOWN);
if (startwep_restrictions[i] == CC_BANNED)
continue;
if (startwep_restrictions[i] == CC_UNRESTRICTED)
textcolor(LIGHTGREY);
else
textcolor(DARKGREY);
const char letter = 'a' + i;
cprintf("%c - %s" EOL, letter,
startwep[i] == WPN_UNARMED ? "claws"
: weapon_base_name(startwep[i]));
if (Options.prev_weapon == startwep[i])
prevmatch = true;
}
if (!prevmatch && Options.prev_weapon != WPN_RANDOM)
Options.prev_weapon = WPN_UNKNOWN;
textcolor(BROWN);
cprintf(EOL "* - Random choice; + - Good random choice; "
"Bksp - Back to species and class selection; "
"X - Quit" EOL);
if (prevmatch || Options.prev_weapon == WPN_RANDOM)
{
cprintf("; Enter - %s",
Options.prev_weapon == WPN_RANDOM ? "Random" :
Options.prev_weapon == WPN_UNARMED ? "claws" :
weapon_base_name(Options.prev_weapon));
}
cprintf(EOL);
do
{
textcolor( CYAN );
cprintf(EOL "Which weapon? ");
textcolor( LIGHTGREY );
keyin = c_getch();
switch (keyin)
{
case 'X':
cprintf(EOL "Goodbye!");
end(0);
break;
case CK_BKSP:
case CK_ESCAPE:
case ' ':
return (false);
case '\r':
case '\n':
if (Options.prev_weapon != WPN_UNKNOWN)
{
if (Options.prev_weapon == WPN_RANDOM)
keyin = '*';
else
{
for (int i = 0; i < num_choices; ++i)
if (startwep[i] == Options.prev_weapon)
keyin = 'a' + i;
}
}
break;
case '%':
list_commands('%');
return _choose_weapon();
default:
break;
}
}
while (keyin != '*' && keyin != '+'
&& (keyin < 'a' || keyin >= ('a' + num_choices)
|| startwep_restrictions[keyin - 'a'] == CC_BANNED));
}
if (Options.random_pick || Options.weapon == WPN_RANDOM
|| keyin == '*' || keyin == '+')
{
Options.weapon = WPN_RANDOM;
ng_weapon = WPN_RANDOM;
int good_choices = 0;
if (keyin == '+' || Options.good_random && keyin != '*')
{
for (int i = 0; i < num_choices; i++)
{
if (weapon_restriction(startwep[i], ng) == CC_UNRESTRICTED
&& one_chance_in(++good_choices))
{
keyin = i;
}
}
}
if (!good_choices)
keyin = random2(num_choices);
keyin += 'a';
}
else
ng_weapon = startwep[keyin - 'a'];
if (startwep[keyin - 'a'] == WPN_UNARMED)
you.inv[0].quantity = 0; // no weapon
else
you.inv[0].sub_type = startwep[keyin - 'a'];
return (true);
}
static bool _necromancy_okay()
{
switch (you.species)
{
case SP_DEEP_ELF:
case SP_SLUDGE_ELF:
case SP_DEEP_DWARF:
case SP_DEMONSPAWN:
case SP_KENKU:
case SP_MUMMY:
case SP_VAMPIRE:
return (true);
default:
if (player_genus(GENPC_DRACONIAN))
return (true);
return (false);
}
}
static void _init_player(void)
{
you.init();
}
static void _give_last_paycheck(job_type which_job)
{
switch (which_job)
{
case JOB_HEALER:
case JOB_THIEF:
you.gold = 100;
break;
case JOB_WANDERER:
case JOB_WARPER:
case JOB_ARCANE_MARKSMAN:
case JOB_ASSASSIN:
you.gold = 50;
break;
default:
you.gold = 20;
break;
case JOB_PALADIN:
case JOB_MONK:
you.gold = 0;
break;
}
}
// Requires stuff::modify_all_stats() and works because
// stats zeroed out by newgame::init_player()... Recall
// that demonspawn & demigods get more later on. {dlb}
static void _species_stat_init(species_type which_species)
{
int sb = 0; // strength base
int ib = 0; // intelligence base
int db = 0; // dexterity base
// Note: The stats in in this list aren't intended to sum the same
// for all races. The fact that Mummies and Ghouls are really low
// is considered acceptable (Mummies don't have to eat, and Ghouls
// are supposed to be a really hard race). -- bwr
switch (which_species)
{
default: sb = 6; ib = 6; db = 6; break; // 18
case SP_HUMAN: sb = 6; ib = 6; db = 6; break; // 18
case SP_DEMIGOD: sb = 9; ib = 10; db = 9; break; // 28
case SP_DEMONSPAWN: sb = 6; ib = 7; db = 6; break; // 19
case SP_HIGH_ELF: sb = 5; ib = 9; db = 8; break; // 22
case SP_DEEP_ELF: sb = 3; ib = 10; db = 8; break; // 21
case SP_SLUDGE_ELF: sb = 6; ib = 7; db = 7; break; // 20
case SP_MOUNTAIN_DWARF: sb = 9; ib = 4; db = 5; break; // 18
case SP_DEEP_DWARF: sb = 9; ib = 6; db = 6; break; // 21
case SP_TROLL: sb = 13; ib = 2; db = 3; break; // 18
case SP_OGRE: sb = 10; ib = 5; db = 3; break; // 18
case SP_MINOTAUR: sb = 10; ib = 3; db = 3; break; // 16
case SP_HILL_ORC: sb = 9; ib = 3; db = 4; break; // 16
case SP_CENTAUR: sb = 8; ib = 5; db = 2; break; // 15
case SP_NAGA: sb = 8; ib = 6; db = 4; break; // 18
case SP_MERFOLK: sb = 6; ib = 5; db = 7; break; // 18
case SP_KENKU: sb = 6; ib = 6; db = 7; break; // 19
case SP_KOBOLD: sb = 5; ib = 4; db = 8; break; // 17
case SP_HALFLING: sb = 3; ib = 6; db = 9; break; // 18
case SP_SPRIGGAN: sb = 2; ib = 7; db = 9; break; // 18
case SP_MUMMY: sb = 7; ib = 3; db = 3; break; // 13
case SP_GHOUL: sb = 9; ib = 1; db = 2; break; // 13
case SP_VAMPIRE: sb = 5; ib = 8; db = 7; break; // 20
case SP_RED_DRACONIAN:
case SP_WHITE_DRACONIAN:
case SP_GREEN_DRACONIAN:
case SP_YELLOW_DRACONIAN:
case SP_GREY_DRACONIAN:
case SP_BLACK_DRACONIAN:
case SP_PURPLE_DRACONIAN:
case SP_MOTTLED_DRACONIAN:
case SP_PALE_DRACONIAN:
case SP_BASE_DRACONIAN: sb = 9; ib = 6; db = 2; break; // 17
}
modify_all_stats( sb+2, ib+2, db+2 );
}
static void _jobs_stat_init(job_type which_job)
{
int s = 0; // strength mod
int i = 0; // intelligence mod
int d = 0; // dexterity mod
int hp = 0; // HP base
int mp = 0; // MP base
// Note: Wanderers are correct, they're a challenging class. -- bwr
switch (which_job)
{
case JOB_FIGHTER: s = 8; i = 0; d = 4; hp = 15; mp = 0; break;
case JOB_BERSERKER: s = 9; i = -1; d = 4; hp = 15; mp = 0; break;
case JOB_GLADIATOR: s = 7; i = 0; d = 5; hp = 14; mp = 0; break;
case JOB_PALADIN: s = 7; i = 2; d = 3; hp = 14; mp = 0; break;
case JOB_CRUSADER: s = 4; i = 4; d = 4; hp = 13; mp = 1; break;
case JOB_DEATH_KNIGHT: s = 5; i = 3; d = 4; hp = 13; mp = 1; break;
case JOB_CHAOS_KNIGHT: s = 4; i = 4; d = 4; hp = 13; mp = 1; break;
case JOB_REAVER: s = 5; i = 5; d = 2; hp = 13; mp = 1; break;
case JOB_HEALER: s = 5; i = 5; d = 2; hp = 13; mp = 1; break;
case JOB_PRIEST: s = 5; i = 4; d = 3; hp = 12; mp = 1; break;
case JOB_ASSASSIN: s = 3; i = 3; d = 6; hp = 12; mp = 0; break;
case JOB_THIEF: s = 4; i = 2; d = 6; hp = 13; mp = 0; break;
case JOB_STALKER: s = 2; i = 4; d = 6; hp = 12; mp = 1; break;
case JOB_HUNTER: s = 4; i = 3; d = 5; hp = 13; mp = 0; break;
case JOB_WARPER: s = 3; i = 5; d = 4; hp = 12; mp = 1; break;
case JOB_ARCANE_MARKSMAN: s = 3; i = 5; d = 4; hp = 12; mp = 1; break;
case JOB_MONK: s = 3; i = 2; d = 7; hp = 13; mp = 0; break;
case JOB_TRANSMUTER: s = 2; i = 5; d = 5; hp = 12; mp = 1; break;
case JOB_WIZARD: s = -1; i = 10; d = 3; hp = 8; mp = 5; break;
case JOB_CONJURER: s = 0; i = 7; d = 5; hp = 10; mp = 3; break;
case JOB_ENCHANTER: s = 0; i = 7; d = 5; hp = 10; mp = 3; break;
case JOB_FIRE_ELEMENTALIST: s = 0; i = 7; d = 5; hp = 10; mp = 3; break;
case JOB_ICE_ELEMENTALIST: s = 0; i = 7; d = 5; hp = 10; mp = 3; break;
case JOB_AIR_ELEMENTALIST: s = 0; i = 7; d = 5; hp = 10; mp = 3; break;
case JOB_EARTH_ELEMENTALIST:s = 0; i = 7; d = 5; hp = 10; mp = 3; break;
case JOB_SUMMONER: s = 0; i = 7; d = 5; hp = 10; mp = 3; break;
case JOB_VENOM_MAGE: s = 0; i = 7; d = 5; hp = 10; mp = 3; break;
case JOB_NECROMANCER: s = 0; i = 7; d = 5; hp = 10; mp = 3; break;
case JOB_WANDERER:
{
// Wanderers get their stats randomly distributed.
_wanderer_assign_remaining_stats(12);
hp = 11; mp = 1; break;
}
case JOB_ARTIFICER: s = 3; i = 4; d = 5; hp = 13; mp = 1; break;
default: s = 0; i = 0; d = 0; hp = 10; mp = 0; break;
}
modify_all_stats( s, i, d );
// Used for Jiyva's stat swapping if the player has not reached
// experience level 3.
you.last_chosen = (stat_type) random2(NUM_STATS);
set_hp( hp, true );
set_mp( mp, true );
}
void give_basic_mutations(species_type speci)
{
// We should switch over to a size-based system
// for the fast/slow metabolism when we get around to it.
switch (speci)
{
case SP_HILL_ORC:
you.mutation[MUT_SAPROVOROUS] = 1;
break;
case SP_OGRE:
you.mutation[MUT_TOUGH_SKIN] = 1;
you.mutation[MUT_FAST_METABOLISM] = 1;
you.mutation[MUT_SAPROVOROUS] = 1;
break;
case SP_HALFLING:
you.mutation[MUT_SLOW_METABOLISM] = 1;
break;
case SP_MINOTAUR:
you.mutation[MUT_HORNS] = 2;
break;
case SP_SPRIGGAN:
you.mutation[MUT_ACUTE_VISION] = 1;
you.mutation[MUT_FAST] = 3;
you.mutation[MUT_HERBIVOROUS] = 3;
you.mutation[MUT_SLOW_METABOLISM] = 3;
break;
case SP_CENTAUR:
you.mutation[MUT_TOUGH_SKIN] = 3;
you.mutation[MUT_FAST] = 2;
you.mutation[MUT_DEFORMED] = 1;
you.mutation[MUT_FAST_METABOLISM] = 2;
you.mutation[MUT_HOOVES] = 1;
break;
case SP_NAGA:
you.mutation[MUT_ACUTE_VISION] = 1;
you.mutation[MUT_POISON_RESISTANCE] = 1;
you.mutation[MUT_DEFORMED] = 1;
break;
case SP_MUMMY:
you.mutation[MUT_TORMENT_RESISTANCE] = 1;
you.mutation[MUT_POISON_RESISTANCE] = 1;
you.mutation[MUT_COLD_RESISTANCE] = 1;
you.mutation[MUT_NEGATIVE_ENERGY_RESISTANCE] = 3;
break;
case SP_DEEP_DWARF:
you.mutation[MUT_SLOW_HEALING] = 3;
you.mutation[MUT_PASSIVE_MAPPING] = 1;
break;
case SP_GHOUL:
you.mutation[MUT_TORMENT_RESISTANCE] = 1;
you.mutation[MUT_POISON_RESISTANCE] = 1;
you.mutation[MUT_COLD_RESISTANCE] = 1;
you.mutation[MUT_NEGATIVE_ENERGY_RESISTANCE] = 3;
you.mutation[MUT_SAPROVOROUS] = 3;
you.mutation[MUT_CARNIVOROUS] = 3;
you.mutation[MUT_SLOW_HEALING] = 1;
break;
case SP_KENKU:
you.mutation[MUT_BEAK] = 1;
you.mutation[MUT_TALONS] = 1;
break;
case SP_TROLL:
you.mutation[MUT_TOUGH_SKIN] = 2;
you.mutation[MUT_REGENERATION] = 2;
you.mutation[MUT_FAST_METABOLISM] = 3;
you.mutation[MUT_SAPROVOROUS] = 2;
you.mutation[MUT_GOURMAND] = 1;
you.mutation[MUT_SHAGGY_FUR] = 1;
break;
case SP_KOBOLD:
you.mutation[MUT_SAPROVOROUS] = 2;
you.mutation[MUT_CARNIVOROUS] = 3;
break;
case SP_VAMPIRE:
you.mutation[MUT_FANGS] = 3;
you.mutation[MUT_ACUTE_VISION] = 1;
break;
default:
break;
}
// Starting mutations are unremovable.
for (int i = 0; i < NUM_MUTATIONS; ++i)
you.demon_pow[i] = you.mutation[i];
}
// Give knowledge of things that aren't in the starting inventory.
static void _give_basic_knowledge(job_type which_job)
{
if (you.species == SP_VAMPIRE)
set_ident_type(OBJ_POTIONS, POT_BLOOD_COAGULATED, ID_KNOWN_TYPE);
switch (which_job)
{
case JOB_STALKER:
case JOB_ASSASSIN:
case JOB_VENOM_MAGE:
set_ident_type(OBJ_POTIONS, POT_POISON, ID_KNOWN_TYPE);
break;
case JOB_TRANSMUTER:
set_ident_type(OBJ_POTIONS, POT_WATER, ID_KNOWN_TYPE);
break;
case JOB_ARTIFICER:
if (!item_is_rod(you.inv[2]))
set_ident_type(OBJ_SCROLLS, SCR_RECHARGING, ID_KNOWN_TYPE);
break;
default:
break;
}
}
static void _give_basic_spells(job_type which_job)
{
// Wanderers may or may not already have a spell. -- bwr
if (which_job == JOB_WANDERER)
return;
spell_type which_spell = SPELL_NO_SPELL;
switch (which_job)
{
case JOB_WIZARD:
if (!you.skills[SK_CONJURATIONS])
{
// Wizards who start with Minor Magic III (summoning) have no
// Conjurations skill, and thus get another starting spell.
which_spell = SPELL_SUMMON_SMALL_MAMMALS;
break;
}
// intentional fall-through
case JOB_CONJURER:
case JOB_REAVER:
which_spell = SPELL_MAGIC_DART;
break;
case JOB_STALKER:
case JOB_VENOM_MAGE:
which_spell = SPELL_STING;
break;
case JOB_SUMMONER:
which_spell = SPELL_SUMMON_SMALL_MAMMALS;
break;
case JOB_NECROMANCER:
which_spell = SPELL_PAIN;
break;
case JOB_ENCHANTER:
which_spell = SPELL_CORONA;
break;
case JOB_FIRE_ELEMENTALIST:
which_spell = SPELL_FLAME_TONGUE;
break;
case JOB_ICE_ELEMENTALIST:
which_spell = SPELL_FREEZE;
break;
case JOB_AIR_ELEMENTALIST:
which_spell = SPELL_SHOCK;
break;
case JOB_EARTH_ELEMENTALIST:
which_spell = SPELL_SANDBLAST;
break;
case JOB_DEATH_KNIGHT:
if (you.species == SP_DEMIGOD || you.religion != GOD_YREDELEMNUL)
which_spell = SPELL_PAIN;
break;
default:
break;
}
if (which_spell != SPELL_NO_SPELL)
add_spell_to_memory( which_spell );
return;
}
static void _make_rod(item_def &item, stave_type rod_type, int ncharges)
{
item.base_type = OBJ_STAVES;
item.sub_type = rod_type;
item.quantity = 1;
item.special = you.item_description[IDESC_STAVES][rod_type];
item.colour = BROWN;
init_rod_mp(item, ncharges);
}
// Creates an item of a given base and sub type.
// replacement is used when handing out armour that is not wearable for
// some species; otherwise use -1.
static void _newgame_make_item(int slot, equipment_type eqslot,
object_class_type base,
int sub_type, int replacement = -1,
int qty = 1, int plus = 0, int plus2 = 0)
{
if (slot == -1)
{
// If another of the item type is already there, add to the
// stack instead.
for (int i = 0; i < ENDOFPACK; ++i)
{
item_def& item = you.inv[i];
if (item.base_type == base && item.sub_type == sub_type
&& is_stackable_item(item))
{
item.quantity += qty;
return;
}
}
for (int i = 0; i < ENDOFPACK; ++i)
{
if (!you.inv[i].is_valid())
{
slot = i;
break;
}
}
ASSERT(slot != -1);
}
item_def &item(you.inv[slot]);
item.base_type = base;
item.sub_type = sub_type;
item.quantity = qty;
item.plus = plus;
item.plus2 = plus2;
item.special = 0;
// If the character is restricted in wearing armour of equipment
// slot eqslot, hand out replacement instead.
if (item.base_type == OBJ_ARMOUR && replacement != -1
&& !you_can_wear(eqslot))
{
// Don't replace shields with bucklers for large races or
// draconians.
if (sub_type != ARM_SHIELD
|| you.body_size(PSIZE_TORSO) < SIZE_LARGE
&& !player_genus(GENPC_DRACONIAN))
{
item.sub_type = replacement;
}
}
if (eqslot != EQ_NONE && you.equip[eqslot] == -1)
you.equip[eqslot] = slot;
}
// Returns true if a "good" weapon is given.
static bool _give_wanderer_weapon(int & slot, int wpn_skill, int plus)
{
// Darts skill also gets you some needles.
if (wpn_skill == SK_THROWING)
{
// Plus is set if we are getting a good item. In that case, we
// get curare here.
if (plus)
{
_newgame_make_item(slot, EQ_NONE, OBJ_MISSILES, MI_NEEDLE, -1,
1 + random2(4));
set_item_ego_type(you.inv[slot], OBJ_MISSILES, SPMSL_CURARE);
slot++;
}
// Otherwise, we just get some poisoned needles.
else
{
_newgame_make_item(slot, EQ_NONE, OBJ_MISSILES, MI_NEEDLE, -1,
5 + roll_dice(2, 5));
set_item_ego_type(you.inv[slot], OBJ_MISSILES, SPMSL_POISONED);
slot++;
}
}
_newgame_make_item(slot, EQ_WEAPON, OBJ_WEAPONS, WPN_KNIFE);
// We'll also re-fill the template, all for later possible safe
// reuse of code in the future.
you.inv[slot].quantity = 1;
you.inv[slot].plus = 0;
you.inv[slot].plus2 = 0;
you.inv[slot].special = 0;
// Now fill in the type according to the random wpn_skill.
switch (wpn_skill)
{
case SK_SHORT_BLADES:
you.inv[slot].sub_type = WPN_SHORT_SWORD;
break;
case SK_MACES_FLAILS:
you.inv[slot].sub_type = WPN_MACE;
break;
case SK_AXES:
you.inv[slot].sub_type = WPN_HAND_AXE;
break;
case SK_POLEARMS:
you.inv[slot].sub_type = WPN_SPEAR;
break;
case SK_STAVES:
you.inv[slot].sub_type = WPN_QUARTERSTAFF;
break;
case SK_THROWING:
you.inv[slot].sub_type = WPN_BLOWGUN;
break;
case SK_BOWS:
you.inv[slot].sub_type = WPN_BOW;
break;
case SK_CROSSBOWS:
you.inv[slot].sub_type = WPN_CROSSBOW;
break;
}
int offset = plus ? 1 : 0;
you.inv[slot].plus = random2(plus) + offset;
you.inv[slot].plus2 = random2(plus) + offset;
return (true);
}
static void _newgame_clear_item(int slot)
{
you.inv[slot] = item_def();
for (int i = EQ_WEAPON; i < NUM_EQUIP; ++i)
if (you.equip[i] == slot)
you.equip[i] = -1;
}
// The overall role choice for wanderers is a weighted chance based on
// stats.
static stat_type _wanderer_choose_role()
{
int total_stats = 0;
total_stats += you.strength;
total_stats += you.dex;
total_stats += you.intel;
int target = random2(total_stats);
stat_type role;
if (target < you.strength)
role = STAT_STRENGTH;
else if (target < (you.dex + you.strength))
role = STAT_DEXTERITY;
else
role = STAT_INTELLIGENCE;
return (role);
}
static skill_type _apt_weighted_choice(const skill_type * skill_array,
unsigned arr_size)
{
int total_apt = 0;
for (unsigned i = 0; i < arr_size; ++i)
{
int reciprocal_apt = (100 * 100) /
species_skills(skill_array[i], you.species);
total_apt += reciprocal_apt;
}
unsigned probe = random2(total_apt);
unsigned region_covered = 0;
for (unsigned i = 0; i < arr_size; ++i)
{
int reciprocal_apt = (100 * 100) /
species_skills(skill_array[i], you.species);
region_covered += reciprocal_apt;
if (probe < region_covered)
return (skill_array[i]);
}
return (NUM_SKILLS);
}
static skill_type _wanderer_role_skill_select(stat_type selected_role,
skill_type sk_1,
skill_type sk_2)
{
skill_type selected_skill = SK_NONE;
switch((int)selected_role)
{
case STAT_DEXTERITY:
switch (random2(6))
{
case 0:
case 1:
selected_skill = SK_FIGHTING;
break;
case 2:
selected_skill = SK_DODGING;
break;
case 3:
selected_skill = SK_STEALTH;
break;
case 4:
case 5:
selected_skill = sk_1;
break;
}
break;
case STAT_STRENGTH:
{
int options = 3;
if (!you_can_wear(EQ_BODY_ARMOUR))
options--;
switch (random2(options))
{
case 0:
selected_skill = SK_FIGHTING;
break;
case 1:
selected_skill = sk_1;
break;
case 2:
selected_skill = SK_ARMOUR;
break;
}
break;
}
case STAT_INTELLIGENCE:
switch (random2(3))
{
case 0:
selected_skill = SK_SPELLCASTING;
break;
case 1:
selected_skill = sk_1;
break;
case 2:
selected_skill = sk_2;
break;
}
break;
}
return (selected_skill);
}
static skill_type _wanderer_role_weapon_select(stat_type role)
{
skill_type skill = NUM_SKILLS;
const skill_type str_weapons[] =
{ SK_AXES, SK_MACES_FLAILS, SK_BOWS, SK_CROSSBOWS };
int str_size = sizeof(str_weapons) / sizeof(skill_type);
const skill_type dex_weapons[] =
{ SK_SHORT_BLADES, SK_STAVES, SK_UNARMED_COMBAT, SK_POLEARMS };
int dex_size = sizeof(dex_weapons) / sizeof(skill_type);
const skill_type casting_schools[] =
{ SK_SUMMONINGS, SK_NECROMANCY, SK_TRANSLOCATIONS,
SK_TRANSMUTATIONS, SK_POISON_MAGIC, SK_CONJURATIONS,
SK_ENCHANTMENTS, SK_FIRE_MAGIC, SK_ICE_MAGIC,
SK_AIR_MAGIC, SK_EARTH_MAGIC };
int casting_size = sizeof(casting_schools) / sizeof(skill_type);
switch ((int)role)
{
case STAT_STRENGTH:
skill = _apt_weighted_choice(str_weapons, str_size);
break;
case STAT_DEXTERITY:
skill = _apt_weighted_choice(dex_weapons, dex_size);
break;
case STAT_INTELLIGENCE:
skill = _apt_weighted_choice(casting_schools, casting_size);
break;
}
return (skill);
}
static void _wanderer_role_skill(stat_type role, int levels)
{
skill_type weapon_type = NUM_SKILLS;
skill_type spell2 = NUM_SKILLS;
weapon_type = _wanderer_role_weapon_select(role);
if (role == STAT_INTELLIGENCE)
spell2 = _wanderer_role_weapon_select(role);
skill_type selected_skill = NUM_SKILLS;
for (int i = 0; i < levels; ++i)
{
selected_skill = _wanderer_role_skill_select(role, weapon_type,
spell2);
you.skills[selected_skill]++;
}
}
// Select a random skill from all skills we have at least 1 level in.
static skill_type _weighted_skill_roll()
{
int total_skill = 0;
for (unsigned i = 0; i < NUM_SKILLS; ++i)
total_skill += you.skills[i];
int probe = random2(total_skill);
int covered_region = 0;
for (unsigned i = 0; i < NUM_SKILLS; ++i)
{
covered_region += you.skills[i];
if (probe < covered_region)
return (skill_type(i));
}
return (NUM_SKILLS);
}
static void _give_wanderer_book(skill_type skill, int & slot)
{
int book_type = BOOK_MINOR_MAGIC_I;
switch((int)skill)
{
case SK_SPELLCASTING:
switch (random2(3))
{
case 0:
book_type = BOOK_MINOR_MAGIC_I;
break;
case 1:
book_type = BOOK_MINOR_MAGIC_II;
break;
case 2:
book_type = BOOK_MINOR_MAGIC_III;
break;
}
break;
case SK_CONJURATIONS:
switch (random2(6))
{
case 0:
book_type = BOOK_MINOR_MAGIC_I;
break;
case 1:
book_type = BOOK_MINOR_MAGIC_II;
break;
case 2:
book_type = BOOK_CONJURATIONS_I;
break;
case 3:
book_type = BOOK_CONJURATIONS_II;
break;
case 4:
book_type = BOOK_YOUNG_POISONERS;
break;
case 5:
book_type = BOOK_STALKING;
break;
}
break;
case SK_SUMMONINGS:
switch (random2(2))
{
case 0:
book_type = BOOK_MINOR_MAGIC_III;
break;
case 1:
book_type = BOOK_CALLINGS;
break;
}
break;
case SK_NECROMANCY:
book_type = BOOK_NECROMANCY;
break;
case SK_TRANSLOCATIONS:
book_type = BOOK_SPATIAL_TRANSLOCATIONS;
break;
case SK_TRANSMUTATIONS:
switch (random2(2))
{
case 0:
book_type = BOOK_GEOMANCY;
break;
case 1:
book_type = BOOK_CHANGES;
break;
}
break;
case SK_FIRE_MAGIC:
switch (random2(3))
{
case 0:
book_type = BOOK_MINOR_MAGIC_I;
break;
case 1:
book_type = BOOK_FLAMES;
break;
case 2:
book_type = BOOK_CONJURATIONS_I;
break;
}
break;
case SK_ICE_MAGIC:
switch (random2(3))
{
case 0:
book_type = BOOK_MINOR_MAGIC_II;
break;
case 1:
book_type = BOOK_FROST;
break;
case 2:
book_type = BOOK_CONJURATIONS_II;
break;
}
break;
case SK_AIR_MAGIC:
book_type = BOOK_AIR;
break;
case SK_EARTH_MAGIC:
book_type = BOOK_GEOMANCY;
break;
case SK_POISON_MAGIC:
switch (random2(2))
{
case 0:
book_type = BOOK_STALKING;
break;
case 1:
book_type = BOOK_YOUNG_POISONERS;
break;
}
break;
case SK_ENCHANTMENTS:
switch (random2(2))
{
case 0:
book_type = BOOK_WAR_CHANTS;
break;
case 1:
book_type = BOOK_CHARMS;
break;
}
break;
}
_newgame_make_item(slot, EQ_NONE, OBJ_BOOKS, book_type);
}
// Players can get some consumables as a "good item".
static void _good_potion_or_scroll(int & slot)
{
int base_rand = 5;
// No potions for mummies.
if (you.is_undead == US_UNDEAD)
base_rand -= 3;
// No berserk rage for ghouls.
else if (you.is_undead && you.is_undead != US_SEMI_UNDEAD)
base_rand--;
you.inv[slot].quantity = 1;
you.inv[slot].plus = 0;
you.inv[slot].plus2 = 0;
switch (random2(base_rand))
{
case 0:
you.inv[slot].base_type = OBJ_SCROLLS;
you.inv[slot].sub_type = SCR_FEAR;
break;
case 1:
you.inv[slot].base_type = OBJ_SCROLLS;
you.inv[slot].sub_type = SCR_BLINKING;
break;
case 2:
you.inv[slot].base_type = OBJ_POTIONS;
you.inv[slot].sub_type = POT_HEAL_WOUNDS;
break;
case 3:
you.inv[slot].base_type = OBJ_POTIONS;
you.inv[slot].sub_type = POT_SPEED;
break;
case 4:
you.inv[slot].base_type = OBJ_POTIONS;
you.inv[slot].sub_type = POT_BERSERK_RAGE;
break;
}
slot++;
}
// Make n random attempts at identifying healing or teleportation.
static void _healing_or_teleport(int n)
{
int temp_rand = 2;
// No potions for mummies.
if (you.is_undead == US_UNDEAD)
temp_rand--;
for (int i = 0; i < n; ++i)
{
switch (random2(temp_rand))
{
case 0:
set_ident_type(OBJ_SCROLLS, SCR_TELEPORTATION, ID_KNOWN_TYPE);
break;
case 1:
set_ident_type(OBJ_POTIONS, POT_HEALING, ID_KNOWN_TYPE);
break;
}
}
}
// Create a random wand/rod of striking in the inventory.
static void _wanderer_random_evokable(int & slot)
{
wand_type selected_wand = WAND_ENSLAVEMENT;
switch (random2(6))
{
case 0:
selected_wand = WAND_ENSLAVEMENT;
break;
case 1:
selected_wand = WAND_CONFUSION;
break;
case 2:
selected_wand = WAND_FLAME;
break;
case 3:
selected_wand = WAND_FROST;
break;
case 4:
selected_wand = WAND_MAGIC_DARTS;
break;
case 5:
_make_rod(you.inv[slot], STAFF_STRIKING, 8);
slot++;
return;
}
_newgame_make_item(slot, EQ_NONE, OBJ_WANDS, selected_wand, -1, 1,
15);
slot++;
}
void _wanderer_good_equipment(skill_type & skill, int & slot)
{
const skill_type combined_weapon_skills[] =
{ SK_AXES, SK_MACES_FLAILS, SK_BOWS, SK_CROSSBOWS,
SK_SHORT_BLADES, SK_STAVES, SK_UNARMED_COMBAT, SK_POLEARMS };
int total_weapons = sizeof(combined_weapon_skills) / sizeof(skill_type);
// Normalise the input type.
if (skill == SK_FIGHTING)
{
int max_sklev = 0;
skill_type max_skill = SK_NONE;
for (int i = 0; i < total_weapons; ++i)
{
if (you.skills[combined_weapon_skills[i]] >= max_sklev)
{
max_skill = combined_weapon_skills[i];
max_sklev = you.skills[max_skill];
}
}
skill = max_skill;
}
switch((int)skill)
{
case SK_MACES_FLAILS:
case SK_AXES:
case SK_POLEARMS:
case SK_BOWS:
case SK_CROSSBOWS:
case SK_THROWING:
case SK_STAVES:
case SK_SHORT_BLADES:
_give_wanderer_weapon(slot, skill, 3);
slot++;
break;
case SK_ARMOUR:
// Deformed races aren't given armor skill, so there's no need
// to worry about scale mail's not fitting.
_newgame_make_item(slot, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_SCALE_MAIL);
slot++;
break;
case SK_SHIELDS:
_newgame_make_item(slot, EQ_SHIELD, OBJ_ARMOUR, ARM_SHIELD,
ARM_BUCKLER);
slot++;
break;
case SK_SPELLCASTING:
case SK_CONJURATIONS:
case SK_SUMMONINGS:
case SK_NECROMANCY:
case SK_TRANSLOCATIONS:
case SK_TRANSMUTATIONS:
case SK_FIRE_MAGIC:
case SK_ICE_MAGIC:
case SK_AIR_MAGIC:
case SK_EARTH_MAGIC:
case SK_POISON_MAGIC:
case SK_ENCHANTMENTS:
_give_wanderer_book(skill, slot);
slot++;
break;
case SK_DODGING:
case SK_STEALTH:
case SK_TRAPS_DOORS:
case SK_STABBING:
case SK_UNARMED_COMBAT:
case SK_INVOCATIONS:
{
// Random consumables: 2x attempts to ID healing/teleportation
// 1 good potion/scroll.
_healing_or_teleport(2);
_good_potion_or_scroll(slot);
break;
}
case SK_EVOCATIONS:
// Random wand/rod of striking.
_wanderer_random_evokable(slot);
break;
}
}
// The "decent" spell type item puts a spell in the player's memory.
static void _give_wanderer_spell(skill_type skill)
{
spell_type spell = SPELL_NO_SPELL;
// Doing a rejection loop for this because I am lazy.
while (skill == SK_SPELLCASTING)
{
int value = SK_POISON_MAGIC-SK_CONJURATIONS + 1;
skill = skill_type(SK_CONJURATIONS + random2(value));
}
switch ((int)skill)
{
case SK_CONJURATIONS:
spell = SPELL_MAGIC_DART;
break;
case SK_SUMMONINGS:
spell = SPELL_SUMMON_SMALL_MAMMALS;
break;
case SK_NECROMANCY:
spell = SPELL_PAIN;
break;
case SK_TRANSLOCATIONS:
spell = SPELL_APPORTATION;
break;
case SK_TRANSMUTATIONS:
spell = SPELL_SANDBLAST;
break;
case SK_FIRE_MAGIC:
spell = SPELL_FLAME_TONGUE;
break;
case SK_ICE_MAGIC:
spell = SPELL_FREEZE;
break;
case SK_AIR_MAGIC:
spell = SPELL_SHOCK;
break;
case SK_EARTH_MAGIC:
spell = SPELL_SANDBLAST;
break;
case SK_POISON_MAGIC:
spell = SPELL_STING;
break;
case SK_ENCHANTMENTS:
spell = SPELL_CORONA;
break;
}
add_spell_to_memory(spell);
}
void _wanderer_decent_equipment(skill_type & skill,
std::set<skill_type> & gift_skills,
int & slot)
{
const skill_type combined_weapon_skills[] =
{ SK_AXES, SK_MACES_FLAILS, SK_BOWS, SK_CROSSBOWS,
SK_SHORT_BLADES, SK_STAVES, SK_UNARMED_COMBAT, SK_POLEARMS };
int total_weapons = sizeof(combined_weapon_skills) / sizeof(skill_type);
// If we already gave an item for this type, just give the player
// a consumable.
if ((skill == SK_DODGING || skill == SK_STEALTH)
&& gift_skills.find(SK_ARMOUR) != gift_skills.end())
{
skill = SK_TRAPS_DOORS;
}
// Give the player knowledge of only one spell.
if (skill >= SK_SPELLCASTING && skill <= SK_POISON_MAGIC)
{
for (unsigned i = 0; i < you.spells.size(); ++i)
{
if (you.spells[i] != SPELL_NO_SPELL)
{
skill = SK_TRAPS_DOORS;
break;
}
}
}
// If fighting comes up, give something from the highest weapon
// skill.
if (skill == SK_FIGHTING)
{
int max_sklev = 0;
skill_type max_skill = SK_NONE;
for (int i = 0; i < total_weapons; ++i)
{
if (you.skills[combined_weapon_skills[i]] >= max_sklev)
{
max_skill = combined_weapon_skills[i];
max_sklev = you.skills[max_skill];
}
}
skill = max_skill;
}
// Don't give a gift from the same skill twice; just default to
// a healing potion/teleportation scroll.
if (gift_skills.find(skill) != gift_skills.end())
skill = SK_TRAPS_DOORS;
switch((int)skill)
{
case SK_MACES_FLAILS:
case SK_AXES:
case SK_POLEARMS:
case SK_BOWS:
case SK_CROSSBOWS:
case SK_THROWING:
case SK_STAVES:
case SK_SHORT_BLADES:
_give_wanderer_weapon(slot, skill, 0);
slot++;
break;
case SK_ARMOUR:
_newgame_make_item(slot, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_RING_MAIL);
slot++;
break;
case SK_SHIELDS:
_newgame_make_item(slot, EQ_SHIELD, OBJ_ARMOUR, ARM_BUCKLER,
ARM_SHIELD);
slot++;
break;
case SK_DODGING:
case SK_STEALTH:
_newgame_make_item(slot, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
slot++;
break;
case SK_SPELLCASTING:
case SK_CONJURATIONS:
case SK_SUMMONINGS:
case SK_NECROMANCY:
case SK_TRANSLOCATIONS:
case SK_TRANSMUTATIONS:
case SK_FIRE_MAGIC:
case SK_ICE_MAGIC:
case SK_AIR_MAGIC:
case SK_EARTH_MAGIC:
case SK_POISON_MAGIC:
_give_wanderer_spell(skill);
break;
case SK_TRAPS_DOORS:
case SK_STABBING:
case SK_UNARMED_COMBAT:
case SK_INVOCATIONS:
case SK_EVOCATIONS:
_healing_or_teleport(1);
break;
}
}
// We don't actually want to send adventurers wandering naked into the
// dungeon.
static void _wanderer_cover_equip_holes(int & slot)
{
// We are going to cover any glaring holes (no armor/no weapon) that
// occurred during equipment generation.
if (you.equip[EQ_BODY_ARMOUR] == -1)
{
_newgame_make_item(slot, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
slot++;
}
if (you.equip[EQ_WEAPON] == -1)
{
weapon_type weapon = WPN_CLUB;
if (you.dex > you.strength || you.skills[SK_STABBING])
weapon = WPN_DAGGER;
_newgame_make_item(slot, EQ_WEAPON, OBJ_WEAPONS, weapon);
slot++;
}
// Give a dagger if you have stabbing skill. Maybe this is
// unnecessary?
if (you.skills[SK_STABBING])
{
bool has_dagger = false;
for (int i = 0; i < slot; ++i)
{
if (you.inv[i].base_type == OBJ_WEAPONS
&& you.inv[i].sub_type == WPN_DAGGER)
{
has_dagger = true;
break;
}
}
if (!has_dagger)
{
_newgame_make_item(slot, EQ_WEAPON, OBJ_WEAPONS, WPN_DAGGER);
slot++;
}
}
// The player needs a stack of bolts if they have a crossbow.
bool need_bolts = false;
for (int i = 0; i < slot; ++i)
{
if (you.inv[i].base_type == OBJ_WEAPONS
&& you.inv[i].sub_type == WPN_CROSSBOW)
{
need_bolts = true;
break;
}
}
if (need_bolts)
{
_newgame_make_item(slot, EQ_NONE, OBJ_MISSILES, MI_BOLT, -1,
15 + random2avg(21, 5));
slot++;
}
// And the player needs arrows if they have a bow.
bool needs_arrows = false;
for (int i = 0; i < slot; ++i)
{
if (you.inv[i].base_type == OBJ_WEAPONS
&& you.inv[i].sub_type == WPN_BOW)
{
needs_arrows = true;
break;
}
}
if (needs_arrows)
{
_newgame_make_item(slot, EQ_NONE, OBJ_MISSILES, MI_ARROW, -1,
15 + random2avg(21, 5));
slot++;
}
}
// New style wanderers are supposed to be decent in terms of skill
// levels/equipment, but pretty randomised.
static void _create_wanderer(void)
{
// Decide what our character roles are.
stat_type primary_role = _wanderer_choose_role();
stat_type secondary_role = _wanderer_choose_role();
// Regardless of roles, players get a couple levels in these skills.
const skill_type util_skills[] =
{ SK_THROWING, SK_STABBING, SK_TRAPS_DOORS, SK_STEALTH,
SK_SHIELDS, SK_EVOCATIONS, SK_INVOCATIONS };
int util_size = sizeof(util_skills) / sizeof(skill_type);
// No Invocations for demigods.
if (you.species == SP_DEMIGOD)
util_size--;
// Maybe too many skill levels, given the level 1 floor on skill
// levels for wanderers?
int primary_skill_levels = 5;
int secondary_skill_levels = 3;
// Allocate main skill levels.
_wanderer_role_skill(primary_role, primary_skill_levels);
_wanderer_role_skill(secondary_role, secondary_skill_levels);
skill_type util_skill1 = _apt_weighted_choice(util_skills, util_size);
skill_type util_skill2 = _apt_weighted_choice(util_skills, util_size);
// And a couple levels of utility skills.
you.skills[util_skill1]++;
you.skills[util_skill2]++;
// Caster types maybe need more MP?
int mp_adjust = 0;
if (primary_role == STAT_INTELLIGENCE)
mp_adjust++;
if (secondary_role == STAT_INTELLIGENCE)
mp_adjust++;
set_mp(you.magic_points + mp_adjust, true);
// Keep track of what skills we got items from, mostly to prevent
// giving a good and then a normal version of the same weapon.
std::set<skill_type> gift_skills;
// Wanderers get 1 good thing, a couple average things, and then
// 1 last stage to fill any glaring equipment holes (no clothes,
// etc.).
skill_type good_equipment = _weighted_skill_roll();
// The first of these goes through the whole role/aptitude weighting
// thing again. It's quite possible that this will give something
// we have no skill in.
stat_type selected_role = one_chance_in(3) ? secondary_role : primary_role;
skill_type sk_1 = SK_NONE;
skill_type sk_2 = SK_NONE;
sk_1 = _wanderer_role_weapon_select(selected_role);
if (selected_role == STAT_INTELLIGENCE)
sk_2 = _wanderer_role_weapon_select(selected_role);
skill_type decent_1 = _wanderer_role_skill_select(selected_role,
sk_1, sk_2);
skill_type decent_2 = _weighted_skill_roll();
// Not even trying to put things in the same slot from game to game.
int equip_slot = 0;
_wanderer_good_equipment(good_equipment, equip_slot);
gift_skills.insert(good_equipment);
_wanderer_decent_equipment(decent_1, gift_skills, equip_slot);
gift_skills.insert(decent_1);
_wanderer_decent_equipment(decent_2, gift_skills, equip_slot);
gift_skills.insert(decent_2);
_wanderer_cover_equip_holes(equip_slot);
}
// choose_race returns true if the player should also pick a class.
// This is done because of the '!' option which will pick a random
// character, obviating the necessity of choosing a class.
bool choose_race()
{
char keyn;
bool printed = false;
if (Options.cls)
{
you.char_class = get_class(letter_to_index(Options.cls));
ng_cls = Options.cls;
}
if (Options.race != 0)
printed = true;
spec_query:
bool prevraceok = (Options.prev_race == '*');
if (!printed)
{
clrscr();
if (you.char_class != JOB_UNKNOWN)
{
textcolor( BROWN );
bool shortgreet = false;
if (!you.your_name.empty() || you.char_class != JOB_UNKNOWN)
cprintf("Welcome, ");
else
{
cprintf("Welcome.");
shortgreet = true;
}
textcolor( YELLOW );
if (!you.your_name.empty())
{
cprintf("%s", you.your_name.c_str());
if (you.char_class != JOB_UNKNOWN)
cprintf(" the ");
}
if (you.char_class != JOB_UNKNOWN)
cprintf("%s", get_class_name(you.char_class));
if (!shortgreet)
cprintf(".");
textcolor( WHITE ); // for the tutorial
}
else
{
textcolor( WHITE );
cprintf("You must be new here!");
}
cprintf(" (Press Ctrl-T to enter a tutorial.)");
cprintf(EOL EOL);
textcolor( CYAN );
cprintf("You can be: "
"(Press ? for more information, %% for a list of aptitudes)");
cprintf(EOL);
textcolor( LIGHTGREY );
int i = -1;
for (i = 0; i < ng_num_species(); ++i)
{
const species_type si = get_species(i);
if (!_is_species_valid_choice(si))
continue;
// Dim text for restricted species
if (you.char_class == JOB_UNKNOWN
|| class_allowed(si, you.char_class) == CC_UNRESTRICTED)
{
textcolor(LIGHTGREY);
}
else
textcolor(DARKGREY);
// Show banned races as "unavailable".
if (you.char_class != JOB_UNKNOWN
&& class_allowed(si, you.char_class) == CC_BANNED)
{
cprintf(" %s N/A", species_name(si, 1).c_str());
}
else
{
char sletter = index_to_letter(i);
if (sletter == Options.prev_race)
prevraceok = true;
cprintf("%c - %s", sletter, species_name(si, 1).c_str());
}
if (i % 2)
cprintf(EOL);
else
cgotoxy(31, wherey());
textcolor(LIGHTGREY); // Reset text colour.
}
if (i % 2)
cprintf(EOL);
textcolor( BROWN );
cprintf(EOL EOL);
if (you.char_class == JOB_UNKNOWN)
{
cprintf("Space - Choose job first; * - Random species" EOL
"! - Random character; # - Good random character; X - Quit"
EOL);
}
else
{
cprintf("* - Random; + - Good random; "
"Bksp - Back to job selection; X - Quit"
EOL);
}
if (Options.prev_race)
{
if (prevraceok)
{
cprintf("Enter - %s",
_get_opt_race_name(Options.prev_race).c_str());
}
if (_prev_startup_options_set())
{
cprintf("%sTab - %s",
prevraceok? "; " : "",
_prev_startup_description().c_str());
}
cprintf(EOL);
}
textcolor( CYAN );
cprintf(EOL "Which one? ");
textcolor( LIGHTGREY );
printed = true;
}
if (Options.race != 0)
keyn = Options.race;
else
keyn = c_getch();
bool good_random = false;
switch (keyn)
{
case 'X':
case ESCAPE:
cprintf(EOL "Goodbye!");
end(0);
break;
case CK_BKSP:
case ' ':
you.species = SP_UNKNOWN;
Options.race = 0;
return (true);
case '#':
good_random = true;
// intentional fall-through
case '!':
_pick_random_species_and_class(good_random);
Options.random_pick = true; // used to give random weapon/god as well
ng_random = true;
if (good_random)
Options.good_random = true;
return (false);
case '\r':
case '\n':
if (Options.prev_race && prevraceok)
keyn = Options.prev_race;
break;
case '\t':
if (_prev_startup_options_set())
{
if (Options.prev_randpick
|| Options.prev_race == '*' && Options.prev_cls == '*')
{
Options.random_pick = true;
ng_random = true;
_pick_random_species_and_class(Options.good_random);
return (false);
}
_set_startup_options();
you.species = SP_UNKNOWN;
you.char_class = JOB_UNKNOWN;
return (true);
}
break;
// access to the help files
case '?':
list_commands('1');
return choose_race();
case '%':
list_commands('%');
return choose_race();
default:
break;
}
// These are handled specially as they _could_ be set
// using Options.race or prev_race.
if (keyn == CONTROL('T') || keyn == 'T') // easy to set in init.txt
return !pick_tutorial();
bool good_randrace = (keyn == '+');
bool randrace = (good_randrace || keyn == '*');
if (randrace)
{
if (you.char_class == JOB_THIEF || you.char_class == JOB_WANDERER)
good_randrace = false;
int index;
do
{
index = random2(ng_num_species());
}
while (!_is_species_valid_choice(get_species(index), false)
|| you.char_class != JOB_UNKNOWN
&& !is_good_combination(get_species(index), you.char_class,
good_randrace));
keyn = index_to_letter(index);
}
if (keyn >= 'a' && keyn <= 'z' || keyn >= 'A' && keyn <= 'Z')
you.species = get_species(letter_to_index(keyn));
if (!_is_species_valid_choice( you.species ))
{
if (Options.race != 0)
{
Options.race = 0;
printed = false;
}
goto spec_query;
}
if (you.species != SP_UNKNOWN && you.char_class != JOB_UNKNOWN
&& !class_allowed(you.species, you.char_class))
{
goto spec_query;
}
// Set to 0 in case we come back from choose_class().
Options.race = 0;
ng_race = (randrace ? (good_randrace? '+' : '*')
: keyn);
return (true);
}
// Returns true if a class was chosen, false if we should go back to
// race selection.
bool choose_class(void)
{
char keyn;
bool printed = false;
if (Options.cls != 0)
printed = true;
if (you.species != SP_UNKNOWN && you.char_class != JOB_UNKNOWN)
return (true);
ng_cls = 0;
job_query:
bool prevclassok = (Options.prev_cls == '*');
if (!printed)
{
clrscr();
if (you.species != SP_UNKNOWN)
{
textcolor( BROWN );
cprintf("Welcome, ");
textcolor( YELLOW );
if (!you.your_name.empty())
cprintf("%s the ", you.your_name.c_str());
cprintf("%s.", species_name(you.species, 1).c_str());
textcolor( WHITE );
}
else
{
textcolor( WHITE );
cprintf("You must be new here!");
}
cprintf(" (Press Ctrl-T to enter a tutorial.)");
cprintf(EOL EOL);
textcolor( CYAN );
cprintf("You can be: "
"(Press ? for more information, %% for a list of aptitudes)" EOL);
textcolor( LIGHTGREY );
int j = 0;
job_type which_job;
for (int i = 0; i < ng_num_classes(); i++)
{
which_job = get_class(i);
// Dim text for restricted classes.
// Thief and wanderer are general challenge classes in that there's
// no species that's unrestricted in combination with them.
if (you.species == SP_UNKNOWN
&& which_job != JOB_THIEF && which_job != JOB_WANDERER
|| you.species != SP_UNKNOWN
&& class_allowed(you.species, which_job) == CC_UNRESTRICTED)
{
textcolor(LIGHTGREY);
}
else
textcolor(DARKGREY);
// Show banned races as "unavailable".
if (class_allowed(you.species, which_job) == CC_BANNED)
{
cprintf(" %s N/A", get_class_name(which_job));
}
else
{
char sletter = index_to_letter(i);
if (sletter == Options.prev_cls)
prevclassok = true;
cprintf("%c - %s", sletter, get_class_name(which_job));
}
if (j % 2)
cprintf(EOL);
else
cgotoxy(31, wherey());
textcolor(LIGHTGREY); // Reset text colour.
j++;
}
if (j % 2)
cprintf(EOL);
textcolor( BROWN );
if (you.species == SP_UNKNOWN)
{
cprintf(EOL
"Space - Choose species first; * - Random job; "
"+ - Good random job" EOL
"! - Random character; # - Good random character; X - Quit"
EOL);
}
else
{
cprintf(EOL
"* - Random; + - Good random; "
"Bksp - Back to species selection; X - Quit"
EOL);
}
if (Options.prev_cls)
{
if (prevclassok)
{
cprintf("Enter - %s",
_get_opt_class_name(Options.prev_cls).c_str());
}
if (_prev_startup_options_set())
{
cprintf("%sTab - %s",
prevclassok? "; " : "",
_prev_startup_description().c_str());
}
cprintf(EOL);
}
textcolor( CYAN );
cprintf(EOL "Which one? ");
textcolor( LIGHTGREY );
printed = true;
}
if (Options.cls != 0)
keyn = Options.cls;
else
keyn = c_getch();
bool good_random = false;
switch (keyn)
{
case 'X':
cprintf(EOL "Goodbye!");
end(0);
break;
case CK_BKSP:
case ESCAPE:
case ' ':
if (keyn != ' ' || you.species == SP_UNKNOWN)
{
you.char_class = JOB_UNKNOWN;
return (false);
}
case 'T':
case CONTROL('T'):
return pick_tutorial();
case '#':
good_random = true;
// intentional fall-through
case '!':
_pick_random_species_and_class(good_random);
// used to give random weapon/god as well
Options.random_pick = true;
ng_random = true;
if (good_random)
Options.good_random = true;
return (true);
case '\r':
case '\n':
if (Options.prev_cls && prevclassok)
keyn = Options.prev_cls;
break;
case '\t':
if (_prev_startup_options_set())
{
if (Options.prev_randpick
|| Options.prev_race == '*' && Options.prev_cls == '*')
{
Options.random_pick = true;
ng_random = true;
_pick_random_species_and_class(Options.good_random);
return (true);
}
_set_startup_options();
// Toss out old species selection, if any.
you.species = SP_UNKNOWN;
you.char_class = JOB_UNKNOWN;
return (false);
}
break;
// help files
case '?':
list_commands('2');
return choose_class();
case '%':
list_commands('%');
return choose_class();
default:
break;
}
job_type chosen_job = JOB_UNKNOWN;
good_random = (keyn == '+');
if (good_random || keyn == '*')
{
// Pick a job at random... see god retribution for proof this
// is uniform. -- bwr
int job_count = 0;
job_type job;
for (int i = 0; i < NUM_JOBS; i++)
{
job = static_cast<job_type>(i);
if (good_random && (job == JOB_THIEF || job == JOB_WANDERER))
continue;
if (you.species == SP_UNKNOWN
|| is_good_combination(you.species, job, good_random))
{
job_count++;
if (one_chance_in( job_count ))
chosen_job = job;
}
}
ASSERT( chosen_job != JOB_UNKNOWN );
}
else if (keyn >= 'a' && keyn <= 'z' || keyn >= 'A' && keyn <= 'Z')
chosen_job = get_class(letter_to_index(keyn));
if (chosen_job == JOB_UNKNOWN)
{
if (Options.cls != 0)
{
Options.cls = 0;
printed = false;
}
goto job_query;
}
if (you.species != SP_UNKNOWN
&& !class_allowed(you.species, chosen_job))
{
if (Options.cls != 0)
{
Options.cls = 0;
printed = false;
}
goto job_query;
}
ng_cls = keyn;
you.char_class = chosen_job;
return (you.char_class != JOB_UNKNOWN && you.species != SP_UNKNOWN);
}
bool _needs_butchering_tool()
{
// Mummies/Vampires don't eat.
// Ghouls have claws (see below).
if (you.is_undead)
return (false);
// Trolls have claws.
if (you.has_claws())
return (false);
// Spriggans don't eat meat.
if (you.mutation[MUT_HERBIVOROUS] == 3)
return (false);
return (true);
}
static startup_wand_type _wand_to_start(int wand, bool is_rod)
{
if (!is_rod)
{
switch (wand)
{
case WAND_ENSLAVEMENT:
return (SWT_ENSLAVEMENT);
case WAND_CONFUSION:
return (SWT_CONFUSION);
case WAND_MAGIC_DARTS:
return (SWT_MAGIC_DARTS);
case WAND_FROST:
return (SWT_FROST);
case WAND_FLAME:
return (SWT_FLAME);
default:
return (SWT_NO_SELECTION);
}
}
else
{
switch (wand)
{
case STAFF_STRIKING:
return (SWT_STRIKING);
default:
return (SWT_NO_SELECTION);
}
}
}
static int _start_to_wand(int wandtype, bool& is_rod)
{
is_rod = false;
switch (wandtype)
{
case SWT_ENSLAVEMENT:
return (WAND_ENSLAVEMENT);
case SWT_CONFUSION:
return (WAND_CONFUSION);
case SWT_MAGIC_DARTS:
return (WAND_MAGIC_DARTS);
case SWT_FROST:
return (WAND_FROST);
case SWT_FLAME:
return (WAND_FLAME);
case SWT_STRIKING:
is_rod = true;
return (STAFF_STRIKING);
default:
return (-1);
}
}
static bool _choose_wand()
{
// Wand-choosing interface for Artificers -- Greenberg/Bane
const wand_type startwand[5] = { WAND_ENSLAVEMENT, WAND_CONFUSION,
WAND_MAGIC_DARTS, WAND_FROST, WAND_FLAME };
_make_rod(you.inv[2], STAFF_STRIKING, 8);
const int num_choices = 6;
int keyin = 0;
int wandtype;
bool is_rod;
if (Options.wand)
{
if (_start_to_wand(Options.wand, is_rod) != -1)
{
keyin = 'a' + Options.wand - 1;
ng_wand = Options.wand;
goto wand_done;
}
}
if (Options.prev_wand)
{
if (_start_to_wand(Options.prev_wand, is_rod) == -1
&& Options.prev_wand != SWT_RANDOM)
{
Options.prev_wand = SWT_NO_SELECTION;
}
}
if (!Options.random_pick && Options.wand != SWT_RANDOM)
{
_print_character_info();
textcolor( CYAN );
cprintf(EOL "You have a choice of tools:" EOL
"(Press %% for a list of aptitudes)" EOL);
bool prevmatch = false;
for (int i = 0; i < num_choices; i++)
{
textcolor(LIGHTGREY);
const char letter = 'a' + i;
if (i == num_choices - 1)
{
cprintf("%c - %s" EOL, letter,
you.inv[2].name(DESC_QUALNAME, false, true).c_str());
wandtype = you.inv[2].sub_type;
is_rod = true;
}
else
{
cprintf("%c - %s" EOL, letter, wand_type_name(startwand[i]));
wandtype = startwand[i];
is_rod = false;
}
if (Options.prev_wand == _wand_to_start(wandtype, is_rod))
prevmatch = true;
}
if (!prevmatch && Options.prev_wand != SWT_RANDOM)
Options.prev_wand = SWT_NO_SELECTION;
textcolor(BROWN);
cprintf(EOL "* - Random choice; "
"Bksp - Back to species and class selection; "
"X - Quit" EOL);
if (prevmatch || Options.prev_wand == SWT_RANDOM)
{
cprintf("; Enter - %s",
Options.prev_wand == SWT_ENSLAVEMENT ? "Enslavement" :
Options.prev_wand == SWT_CONFUSION ? "Confusion" :
Options.prev_wand == SWT_MAGIC_DARTS ? "Magic Darts" :
Options.prev_wand == SWT_FROST ? "Frost" :
Options.prev_wand == SWT_FLAME ? "Flame" :
Options.prev_wand == SWT_STRIKING ? "Striking" :
Options.prev_wand == SWT_RANDOM ? "Random"
: "Buggy Tool");
}
cprintf(EOL);
do
{
textcolor( CYAN );
cprintf(EOL "Which tool? ");
textcolor( LIGHTGREY );
keyin = c_getch();
switch (keyin)
{
case 'X':
cprintf(EOL "Goodbye!");
end(0);
break;
case CK_BKSP:
case CK_ESCAPE:
case ' ':
return (false);
case '\r':
case '\n':
if (Options.prev_wand != SWT_NO_SELECTION)
{
if (Options.prev_wand == SWT_RANDOM)
keyin = '*';
else
{
for (int i = 0; i < num_choices; ++i)
{
if (i == num_choices - 1)
{
wandtype = you.inv[2].sub_type;
is_rod = true;
}
else
{
wandtype = startwand[i];
is_rod = false;
}
if (Options.prev_wand ==
_wand_to_start(wandtype, is_rod))
{
keyin = 'a' + i;
}
}
}
}
break;
case '%':
list_commands('%');
return _choose_wand();
default:
break;
}
}
while (keyin != '*' && (keyin < 'a' || keyin >= ('a' + num_choices)));
}
if (Options.random_pick || Options.wand == SWT_RANDOM || keyin == '*')
{
Options.wand = WPN_RANDOM;
ng_wand = SWT_RANDOM;
keyin = random2(num_choices);
keyin += 'a';
}
else
ng_wand = keyin - 'a' + 1;
wand_done:
if (keyin - 'a' == num_choices - 1)
{
// Choose the rod; we're all done.
return (true);
}
// 1 wand of random effects and one chosen lesser wand
const wand_type choice = startwand[keyin - 'a'];
const int ncharges = 15;
_newgame_make_item(2, EQ_NONE, OBJ_WANDS, WAND_RANDOM_EFFECTS, -1, 1,
ncharges, 0);
_newgame_make_item(3, EQ_NONE, OBJ_WANDS, choice, -1, 1, ncharges, 0);
return (true);
}
bool _give_items_skills()
{
char keyn;
int weap_skill = 0;
int choice; // used for third-screen choices
ng.init(you); // XXX
switch (you.char_class)
{
case JOB_FIGHTER:
// Equipment.
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_SHORT_SWORD);
if (!_choose_weapon())
return (false);
if (you.inv[0].quantity < 1)
_newgame_clear_item(0);
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_SCALE_MAIL,
ARM_ROBE);
_newgame_make_item(2, EQ_SHIELD, OBJ_ARMOUR, ARM_SHIELD, ARM_BUCKLER);
// Skills.
you.skills[SK_FIGHTING] = 3;
you.skills[SK_SHIELDS] = 2;
weap_skill = 2;
you.skills[(player_light_armour() ? SK_DODGING : SK_ARMOUR)] = 3;
break;
case JOB_GLADIATOR:
{
// Equipment.
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_SHORT_SWORD);
// No unarmed for Trolls/Ghouls this time.
if (!_choose_weapon())
return (false);
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_LEATHER_ARMOUR,
ARM_ANIMAL_SKIN);
_newgame_make_item(2, EQ_SHIELD, OBJ_ARMOUR, ARM_SHIELD, ARM_BUCKLER);
int curr = 3;
if (you_can_wear(EQ_HELMET))
{
_newgame_make_item(3, EQ_HELMET, OBJ_ARMOUR, ARM_HELMET);
curr++;
}
// Small races get stones, the others nets.
if (you.body_size(PSIZE_BODY) < SIZE_MEDIUM)
_newgame_make_item(curr, EQ_NONE, OBJ_MISSILES, MI_STONE, -1, 20);
else
{
_newgame_make_item(curr, EQ_NONE, OBJ_MISSILES, MI_THROWING_NET, -1,
4);
}
// Skills.
you.skills[SK_FIGHTING] = 2;
you.skills[SK_THROWING] = 2;
you.skills[SK_DODGING] = 2;
you.skills[SK_SHIELDS] = 2;
you.skills[SK_UNARMED_COMBAT] = 2;
weap_skill = 3;
break;
}
case JOB_MONK:
you.equip[EQ_WEAPON] = -1; // Monks fight unarmed.
_newgame_make_item(0, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
you.skills[SK_FIGHTING] = 3;
you.skills[SK_UNARMED_COMBAT] = 4;
you.skills[SK_DODGING] = 3;
you.skills[SK_STEALTH] = 2;
break;
case JOB_BERSERKER:
you.religion = GOD_TROG;
you.piety = 35;
// WEAPONS
if (you.has_claws())
you.equip[EQ_WEAPON] = -1; // Trolls/Ghouls fight unarmed.
else
{
// Races skilled with maces/flails get one, the others axes.
weapon_type startwep = WPN_HAND_AXE;
if (species_skills(SK_MACES_FLAILS, you.species) <
species_skills(SK_AXES, you.species))
{
startwep = (player_genus(GENPC_OGRE)) ? WPN_ANKUS : WPN_MACE;
}
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, startwep);
}
// ARMOUR
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ANIMAL_SKIN);
// SKILLS
you.skills[SK_FIGHTING] = 3;
you.skills[SK_DODGING] = 2;
weap_skill = 3;
if (you_can_wear(EQ_BODY_ARMOUR))
you.skills[SK_ARMOUR] = 2;
else
{
you.skills[SK_DODGING]++;
you.skills[SK_ARMOUR] = 1; // for the eventual dragon scale mail :)
}
break;
case JOB_PALADIN:
you.religion = GOD_SHINING_ONE;
you.piety = 28;
// Equipment.
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_FALCHION);
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_RING_MAIL,
ARM_ROBE);
_newgame_make_item(2, EQ_SHIELD, OBJ_ARMOUR, ARM_SHIELD, ARM_BUCKLER);
_newgame_make_item(3, EQ_NONE, OBJ_POTIONS, POT_HEALING);
// Skills.
you.skills[(player_light_armour() ? SK_DODGING : SK_ARMOUR)] = 2;
you.skills[SK_FIGHTING] = 2;
you.skills[SK_SHIELDS] = 2;
you.skills[SK_LONG_BLADES] = 3;
you.skills[SK_INVOCATIONS] = 2;
break;
case JOB_PRIEST:
you.piety = 45;
// Set gods.
if (you.species == SP_DEMONSPAWN || you.species == SP_MUMMY
|| you.species == SP_GHOUL || you.species == SP_VAMPIRE)
{
you.religion = GOD_YREDELEMNUL;
}
else
{
const god_type gods[3] = { GOD_ZIN, GOD_YREDELEMNUL, GOD_BEOGH };
// Disallow invalid choices.
if (religion_restriction(Options.priest, ng) == CC_BANNED)
Options.priest = GOD_NO_GOD;
if (Options.priest != GOD_NO_GOD && Options.priest != GOD_RANDOM)
ng_pr = you.religion = static_cast<god_type>(Options.priest);
else if (Options.random_pick || Options.priest == GOD_RANDOM)
{
bool did_chose = false;
if (Options.good_random)
{
int count = 0;
for (int i = 0; i < 3; i++)
{
if (religion_restriction(gods[i], ng) == CC_BANNED)
continue;
if (religion_restriction(gods[i], ng) == CC_UNRESTRICTED
&& one_chance_in(++count))
{
you.religion = gods[i];
did_chose = true;
}
}
}
if (!did_chose)
{
you.religion = (coinflip() ? GOD_YREDELEMNUL : GOD_ZIN);
// For orcs 50% chance of Beogh instead.
if (you.species == SP_HILL_ORC && coinflip())
you.religion = GOD_BEOGH;
}
ng_pr = GOD_RANDOM;
}
else
{
_print_character_info();
textcolor( CYAN );
cprintf(EOL "Which god do you wish to serve?" EOL);
const char* god_name[3] = {"Zin (for traditional priests)",
"Yredelemnul (for priests of death)",
"Beogh (for priests of Orcs)"};
for (int i = 0; i < 3; i++)
{
if (religion_restriction(gods[i], ng) == CC_BANNED)
continue;
if (religion_restriction(gods[i], ng) == CC_UNRESTRICTED)
textcolor(LIGHTGREY);
else
textcolor(DARKGREY);
const char letter = 'a' + i;
cprintf("%c - %s" EOL, letter, god_name[i]);
}
textcolor( BROWN );
cprintf(EOL "* - Random choice; + - Good random choice" EOL
"Bksp - Back to species and class selection; "
"X - Quit" EOL);
if (religion_restriction(Options.prev_pr, ng) == CC_BANNED)
Options.prev_pr = GOD_NO_GOD;
if (Options.prev_pr != GOD_NO_GOD)
{
textcolor(BROWN);
cprintf(EOL "Enter - %s" EOL,
Options.prev_pr == GOD_ZIN ? "Zin" :
Options.prev_pr == GOD_YREDELEMNUL ? "Yredelemnul" :
Options.prev_pr == GOD_BEOGH ? "Beogh"
: "Random");
}
do
{
keyn = c_getch();
switch (keyn)
{
case 'X':
cprintf(EOL "Goodbye!");
end(0);
break;
case CK_BKSP:
case ESCAPE:
case ' ':
return (false);
case '\r':
case '\n':
if (Options.prev_pr == GOD_NO_GOD
|| Options.prev_pr == GOD_BEOGH
&& you.species != SP_HILL_ORC)
{
break;
}
if (Options.prev_pr != GOD_RANDOM)
{
Options.prev_pr
= static_cast<god_type>(Options.prev_pr);
you.religion = Options.prev_pr;
break;
}
keyn = '*'; // for ng_pr setting
// fall-through for random
case '+':
if (keyn == '+')
{
int count = 0;
for (int i = 0; i < 3; i++)
{
if (religion_restriction(gods[i], ng) == CC_BANNED)
continue;
if (religion_restriction(gods[i], ng)
== CC_UNRESTRICTED
&& one_chance_in(++count))
{
you.religion = gods[i];
}
}
if (count > 0)
break;
}
// intentional fall-through
case '*':
you.religion = coinflip() ? GOD_ZIN : GOD_YREDELEMNUL;
if (you.species == SP_HILL_ORC && coinflip())
you.religion = GOD_BEOGH;
break;
case 'a':
you.religion = GOD_ZIN;
break;
case 'b':
you.religion = GOD_YREDELEMNUL;
break;
case 'c':
if (you.species == SP_HILL_ORC)
{
you.religion = GOD_BEOGH;
break;
} // else fall through
default:
break;
}
}
while (you.religion == GOD_NO_GOD);
ng_pr = (keyn == '*' ? GOD_RANDOM : you.religion);
}
}
if (you.religion == GOD_BEOGH)
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_HAND_AXE);
else
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_QUARTERSTAFF);
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
if (you.religion == GOD_ZIN)
_newgame_make_item(2, EQ_NONE, OBJ_POTIONS, POT_HEALING, -1, 2);
you.skills[SK_FIGHTING] = 2;
you.skills[SK_INVOCATIONS] = 5;
you.skills[SK_DODGING] = 1;
weap_skill = 3;
break;
case JOB_CHAOS_KNIGHT:
{
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_SHORT_SWORD, -1, 1,
2);
if (!_choose_weapon())
return (false);
const god_type gods[3] = { GOD_XOM, GOD_MAKHLEB, GOD_LUGONU };
if (Options.chaos_knight != GOD_NO_GOD
&& Options.chaos_knight != GOD_RANDOM)
{
ng_ck = you.religion =
static_cast<god_type>( Options.chaos_knight );
}
else if (Options.random_pick || Options.chaos_knight == GOD_RANDOM)
{
bool did_chose = false;
if (Options.good_random)
{
int count = 0;
for (int i = 0; i < 3; i++)
{
if (religion_restriction(gods[i], ng) == CC_BANNED)
continue;
if (religion_restriction(gods[i], ng) == CC_UNRESTRICTED
&& one_chance_in(++count))
{
you.religion = gods[i];
did_chose = true;
}
}
}
if (!did_chose)
{
you.religion = (one_chance_in(3) ? GOD_XOM :
coinflip() ? GOD_MAKHLEB
: GOD_LUGONU);
}
ng_ck = GOD_RANDOM;
}
else
{
_print_character_info();
textcolor( CYAN );
cprintf(EOL "Which god of chaos do you wish to serve?" EOL);
const char* god_name[3] = {"Xom of Chaos",
"Makhleb the Destroyer",
"Lugonu the Unformed"};
for (int i = 0; i < 3; i++)
{
if (religion_restriction(gods[i], ng) == CC_BANNED)
continue;
if (religion_restriction(gods[i], ng) == CC_UNRESTRICTED)
textcolor(LIGHTGREY);
else
textcolor(DARKGREY);
const char letter = 'a' + i;
cprintf("%c - %s" EOL, letter, god_name[i]);
}
textcolor( BROWN );
cprintf(EOL "* - Random choice; + - Good random choice" EOL
"Bksp - Back to species and class selection; "
"X - Quit" EOL);
if (Options.prev_ck != GOD_NO_GOD)
{
textcolor(BROWN);
cprintf(EOL "Enter - %s" EOL,
Options.prev_ck == GOD_XOM ? "Xom" :
Options.prev_ck == GOD_MAKHLEB ? "Makhleb" :
Options.prev_ck == GOD_LUGONU ? "Lugonu"
: "Random");
textcolor(LIGHTGREY);
}
do
{
keyn = c_getch();
switch (keyn)
{
case 'X':
cprintf(EOL "Goodbye!");
end(0);
break;
case CK_BKSP:
case CK_ESCAPE:
case ' ':
return (false);
case '\r':
case '\n':
if (Options.prev_ck == GOD_NO_GOD)
break;
if (Options.prev_ck != GOD_RANDOM)
{
you.religion = static_cast<god_type>(Options.prev_ck);
break;
}
keyn = '*'; // for ng_ck setting
// fall-through for random
case '+':
if (keyn == '+')
{
int count = 0;
for (int i = 0; i < 3; i++)
{
if (religion_restriction(gods[i], ng) == CC_BANNED)
continue;
if (religion_restriction(gods[i], ng)
== CC_UNRESTRICTED
&& one_chance_in(++count))
{
you.religion = gods[i];
}
}
if (count > 0)
break;
}
// intentional fall-through
case '*':
you.religion = (one_chance_in(3) ? GOD_XOM :
coinflip() ? GOD_MAKHLEB
: GOD_LUGONU);
break;
case 'a':
you.religion = GOD_XOM;
break;
case 'b':
you.religion = GOD_MAKHLEB;
break;
case 'c':
you.religion = GOD_LUGONU;
break;
default:
break;
}
}
while (you.religion == GOD_NO_GOD);
ng_ck = (keyn == '*') ? GOD_RANDOM
: you.religion;
}
if (you.inv[0].quantity < 1)
_newgame_clear_item(0);
else
you.inv[0].plus2 = 4 - you.inv[0].plus;
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_LEATHER_ARMOUR,
ARM_ROBE, 1, you.religion == GOD_XOM ? 2 : 0);
you.skills[SK_FIGHTING] = 3;
you.skills[SK_ARMOUR] = 1;
you.skills[SK_DODGING] = 1;
if (species_skills(SK_ARMOUR, you.species) >
species_skills(SK_DODGING, you.species))
{
you.skills[SK_DODGING]++;
}
else
you.skills[SK_ARMOUR]++;
weap_skill = 2;
if (you.religion == GOD_XOM)
{
you.skills[SK_FIGHTING]++;
// The new (piety-aware) Xom uses piety in his own special way...
// (Namely, 100 is neutral.)
you.piety = 100;
// The new Xom also uses gift_timeout in his own special way...
// (Namely, a countdown to becoming bored.)
you.gift_timeout = std::max(5, random2(40) + random2(40));
}
else // Makhleb or Lugonu
{
you.skills[SK_INVOCATIONS] = 2;
if (you.religion == GOD_LUGONU)
{
// Chaos Knights of Lugonu start in the Abyss. We need
// to mark this unusual occurrence, so the player
// doesn't get early access to OOD items, etc.
you.char_direction = GDT_GAME_START;
you.piety = 38;
}
else
you.piety = 25;
}
break;
}
case JOB_DEATH_KNIGHT:
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_SHORT_SWORD);
if (!_choose_weapon())
return (false);
choice = DK_NO_SELECTION;
// Order is important here. -- bwr
if (you.species == SP_DEMIGOD)
choice = DK_NECROMANCY;
else if (Options.death_knight != DK_NO_SELECTION
&& Options.death_knight != DK_RANDOM)
{
ng_dk = choice = Options.death_knight;
}
else if (Options.random_pick || Options.death_knight == DK_RANDOM)
{
ng_dk = DK_RANDOM;
bool did_chose = false;
if (Options.good_random)
{
if (_necromancy_okay())
{
choice = DK_NECROMANCY;
did_chose = true;
}
if (religion_restriction(GOD_YREDELEMNUL, ng) == CC_UNRESTRICTED)
{
if (!did_chose || coinflip())
choice = DK_YREDELEMNUL;
did_chose = true;
}
}
if (!did_chose)
choice = (coinflip() ? DK_NECROMANCY : DK_YREDELEMNUL);
}
else
{
_print_character_info();
textcolor(CYAN);
cprintf(EOL "From where do you draw your power?" EOL);
textcolor(_necromancy_okay() ? LIGHTGREY : DARKGREY);
cprintf("a - Necromantic magic" EOL);
// Yredelemnul is an okay choice for everyone.
if (religion_restriction(GOD_YREDELEMNUL, ng) == CC_UNRESTRICTED)
textcolor(LIGHTGREY);
else
textcolor(DARKGREY);
cprintf("b - the god Yredelemnul" EOL);
textcolor( BROWN );
cprintf(EOL "* - Random choice; + - Good random choice " EOL
"Bksp - Back to species and class selection; "
"X - Quit" EOL);
if (Options.prev_dk != DK_NO_SELECTION)
{
textcolor(BROWN);
cprintf(EOL "Enter - %s" EOL,
Options.prev_dk == DK_NECROMANCY ? "Necromancy" :
Options.prev_dk == DK_YREDELEMNUL ? "Yredelemnul"
: "Random");
}
do
{
keyn = c_getch();
switch (keyn)
{
case 'X':
cprintf(EOL "Goodbye!");
end(0);
break;
case CK_BKSP:
case ESCAPE:
case ' ':
return (false);
case '\r':
case '\n':
if (Options.prev_dk == DK_NO_SELECTION)
break;
if (Options.prev_dk != DK_RANDOM)
{
choice = Options.prev_dk;
break;
}
keyn = '*'; // for ng_dk setting
// fall-through for random
case '+':
if (keyn == '+')
{
bool did_chose = false;
if (_necromancy_okay())
{
choice = DK_NECROMANCY;
did_chose = true;
}
if (religion_restriction(GOD_YREDELEMNUL, ng)
== CC_UNRESTRICTED)
{
if (!did_chose || coinflip())
choice = DK_YREDELEMNUL;
did_chose = true;
}
if (did_chose)
break;
}
// fall-through for random
case '*':
choice = coinflip()? DK_NECROMANCY : DK_YREDELEMNUL;
break;
case 'a':
cprintf(EOL "Very well.");
choice = DK_NECROMANCY;
break;
case 'b':
choice = DK_YREDELEMNUL;
default:
break;
}
}
while (choice == DK_NO_SELECTION);
ng_dk = (keyn == '*'? DK_RANDOM : choice);
}
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_LEATHER_ARMOUR,
ARM_ROBE);
switch (choice)
{
default: // This shouldn't happen anyways. -- bwr
case DK_NECROMANCY:
_newgame_make_item(2, EQ_NONE, OBJ_BOOKS, BOOK_NECROMANCY);
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_NECROMANCY] = 2;
break;
case DK_YREDELEMNUL:
you.religion = GOD_YREDELEMNUL;
you.piety = 28;
you.inv[0].plus = 2;
you.inv[0].plus2 = 2;
you.skills[SK_INVOCATIONS] = 3;
break;
}
if (you.inv[0].quantity < 1)
_newgame_clear_item(0);
you.skills[SK_FIGHTING] = 2;
you.skills[SK_ARMOUR] = 1;
you.skills[SK_DODGING] = 1;
weap_skill = 2;
break;
case JOB_HEALER:
you.religion = GOD_ELYVILON;
you.piety = 55;
you.equip[EQ_WEAPON] = -1; // Healers fight unarmed.
_newgame_make_item(0, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(1, EQ_NONE, OBJ_POTIONS, POT_HEALING);
_newgame_make_item(2, EQ_NONE, OBJ_POTIONS, POT_HEAL_WOUNDS);
you.skills[SK_FIGHTING] = 2;
you.skills[SK_UNARMED_COMBAT] = 3;
you.skills[SK_DODGING] = 1;
you.skills[SK_INVOCATIONS] = 4;
break;
case JOB_CRUSADER:
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_SHORT_SWORD);
if (!_choose_weapon())
return (false);
if (you.inv[0].quantity < 1)
_newgame_clear_item(0);
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_LEATHER_ARMOUR,
ARM_ROBE);
_newgame_make_item(2, EQ_NONE, OBJ_BOOKS, BOOK_WAR_CHANTS);
you.skills[SK_FIGHTING] = 3;
you.skills[SK_ARMOUR] = 1;
you.skills[SK_DODGING] = 1;
you.skills[SK_SPELLCASTING] = 2;
you.skills[SK_ENCHANTMENTS] = 2;
weap_skill = 2;
break;
case JOB_REAVER:
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_SHORT_SWORD);
if (!_choose_weapon())
return (false);
if (you.inv[0].quantity < 1)
_newgame_clear_item(0);
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_LEATHER_ARMOUR,
ARM_ROBE);
if (!_choose_book(2, BOOK_CONJURATIONS_I, 2))
return (false);
you.skills[SK_FIGHTING] = 2;
you.skills[SK_ARMOUR] = 1;
you.skills[SK_DODGING] = 1;
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_CONJURATIONS] = 2;
weap_skill = 3;
break;
case JOB_WARPER:
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_SHORT_SWORD);
if (!_choose_weapon())
return (false);
if (you.inv[0].quantity < 1)
_newgame_clear_item(0);
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_LEATHER_ARMOUR,
ARM_ROBE);
_newgame_make_item(2, EQ_NONE, OBJ_BOOKS, BOOK_SPATIAL_TRANSLOCATIONS);
// One free escape.
_newgame_make_item(3, EQ_NONE, OBJ_SCROLLS, SCR_BLINKING);
_newgame_make_item(4, EQ_NONE, OBJ_MISSILES, MI_DART, -1, 20);
you.skills[SK_FIGHTING] = 1;
you.skills[SK_ARMOUR] = 1;
you.skills[SK_DODGING] = 2;
you.skills[SK_SPELLCASTING] = 2;
you.skills[SK_TRANSLOCATIONS] = 3;
you.skills[SK_THROWING] = 1;
weap_skill = 3;
break;
case JOB_ARCANE_MARKSMAN:
_newgame_make_item(0, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
switch (you.species)
{
case SP_HALFLING:
case SP_KOBOLD:
_newgame_make_item(1, EQ_NONE, OBJ_WEAPONS, WPN_SLING);
_newgame_make_item(2, EQ_NONE, OBJ_MISSILES, MI_SLING_BULLET, -1,
30);
// Wield the sling instead.
you.equip[EQ_WEAPON] = 1;
break;
case SP_MOUNTAIN_DWARF:
case SP_DEEP_DWARF:
_newgame_make_item(1, EQ_NONE, OBJ_WEAPONS, WPN_CROSSBOW);
_newgame_make_item(2, EQ_NONE, OBJ_MISSILES, MI_BOLT, -1, 25);
// Wield the crossbow instead.
you.equip[EQ_WEAPON] = 1;
break;
default:
_newgame_make_item(1, EQ_NONE, OBJ_WEAPONS, WPN_BOW);
_newgame_make_item(2, EQ_NONE, OBJ_MISSILES, MI_ARROW, -1, 25);
// Wield the bow instead.
you.equip[EQ_WEAPON] = 1;
break;
}
// And give them a book
_newgame_make_item(3, EQ_NONE, OBJ_BOOKS, BOOK_BRANDS);
you.skills[range_skill(you.inv[1])] = 2;
you.skills[SK_DODGING] = 1;
you.skills[SK_SPELLCASTING] = 2;
you.skills[SK_ENCHANTMENTS] = 2;
break;
case JOB_WIZARD:
_newgame_make_item(0, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(1, EQ_HELMET, OBJ_ARMOUR, ARM_WIZARD_HAT);
if (!_choose_book(2, BOOK_MINOR_MAGIC_I, 3))
return (false);
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
you.skills[SK_SPELLCASTING] = 3;
// All three starting books contain Translocations spells.
you.skills[SK_TRANSLOCATIONS] = 1;
// The other two schools depend on the chosen book.
switch (you.inv[2].sub_type)
{
case BOOK_MINOR_MAGIC_I:
you.skills[SK_CONJURATIONS] = 1;
you.skills[SK_FIRE_MAGIC] = 1;
break;
case BOOK_MINOR_MAGIC_II:
you.skills[SK_CONJURATIONS] = 1;
you.skills[SK_ICE_MAGIC] = 1;
break;
case BOOK_MINOR_MAGIC_III:
you.skills[SK_SUMMONINGS] = 1;
you.skills[SK_ENCHANTMENTS] = 1;
break;
}
break;
case JOB_CONJURER:
_newgame_make_item(0, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
if (!_choose_book(1, BOOK_CONJURATIONS_I, 2))
return (false);
you.skills[SK_CONJURATIONS] = 4;
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
break;
case JOB_ENCHANTER:
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_SHORT_SWORD, -1, 1, 1,
1);
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE, -1, 1, 1);
_newgame_make_item(2, EQ_NONE, OBJ_BOOKS, BOOK_CHARMS);
// Gets some darts - this class is difficult to start off with.
_newgame_make_item(3, EQ_NONE, OBJ_MISSILES, MI_DART, -1, 16, 1);
// Spriggans used to get a rod of striking, but now that anyone
// can get one when playing an Artificer, this is no longer
// necessary. (jpeg)
if (you.species == SP_SPRIGGAN)
you.inv[0].sub_type = WPN_DAGGER;
if (player_genus(GENPC_OGRE) || you.species == SP_TROLL)
you.inv[0].sub_type = WPN_CLUB;
weap_skill = 1;
you.skills[SK_THROWING] = 1;
you.skills[SK_ENCHANTMENTS] = 4;
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
break;
case JOB_SUMMONER:
_newgame_make_item(0, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(1, EQ_NONE, OBJ_BOOKS, BOOK_CALLINGS);
you.skills[SK_SUMMONINGS] = 4;
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
break;
case JOB_NECROMANCER:
_newgame_make_item(0, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(1, EQ_NONE, OBJ_BOOKS, BOOK_NECROMANCY);
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_NECROMANCY] = 4;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
break;
case JOB_TRANSMUTER:
you.equip[EQ_WEAPON] = -1; // Transmuters fight unarmed.
// Some sticks for sticks to snakes.
_newgame_make_item(1, EQ_NONE, OBJ_MISSILES, MI_ARROW, -1, 12);
_newgame_make_item(2, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(3, EQ_NONE, OBJ_BOOKS, BOOK_CHANGES);
// A little bit of starting ammo for evaporate... don't need too
// much now that the character can make their own. - bwr
_newgame_make_item(4, EQ_NONE, OBJ_POTIONS, POT_CONFUSION, -1, 2);
_newgame_make_item(5, EQ_NONE, OBJ_POTIONS, POT_POISON);
// Spriggans used to get a rod of striking, but now that anyone
// can get one when playing an Artificer, this is no longer
// necessary. (jpeg)
you.skills[SK_FIGHTING] = 1;
you.skills[SK_UNARMED_COMBAT] = 3;
you.skills[SK_DODGING] = 2;
you.skills[SK_SPELLCASTING] = 2;
you.skills[SK_TRANSMUTATIONS] = 2;
break;
case JOB_FIRE_ELEMENTALIST:
_newgame_make_item(0, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(1, EQ_NONE, OBJ_BOOKS, BOOK_FLAMES);
you.skills[SK_CONJURATIONS] = 1;
you.skills[SK_FIRE_MAGIC] = 3;
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
break;
case JOB_ICE_ELEMENTALIST:
_newgame_make_item(0, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(1, EQ_NONE, OBJ_BOOKS, BOOK_FROST);
you.skills[SK_CONJURATIONS] = 1;
you.skills[SK_ICE_MAGIC] = 3;
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
break;
case JOB_AIR_ELEMENTALIST:
_newgame_make_item(0, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(1, EQ_NONE, OBJ_BOOKS, BOOK_AIR);
you.skills[SK_CONJURATIONS] = 1;
you.skills[SK_AIR_MAGIC] = 3;
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
break;
case JOB_EARTH_ELEMENTALIST:
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(2, EQ_NONE, OBJ_BOOKS, BOOK_GEOMANCY);
_newgame_make_item(3, EQ_NONE, OBJ_MISSILES, MI_STONE, -1, 20);
you.skills[SK_TRANSMUTATIONS] = 1;
you.skills[SK_EARTH_MAGIC] = 3;
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
break;
case JOB_VENOM_MAGE:
// Venom Mages don't need a starting weapon since acquiring a weapon
// to poison should be easy, and Sting is *powerful*.
_newgame_make_item(0, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(1, EQ_NONE, OBJ_BOOKS, BOOK_YOUNG_POISONERS);
you.skills[SK_POISON_MAGIC] = 4;
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
break;
case JOB_STALKER:
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_DAGGER, -1, 1, 2, 2);
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(2, EQ_CLOAK, OBJ_ARMOUR, ARM_CLOAK);
_newgame_make_item(3, EQ_NONE, OBJ_BOOKS, BOOK_STALKING);
if (player_genus(GENPC_OGRE) || you.species == SP_TROLL)
you.inv[0].sub_type = WPN_CLUB;
weap_skill = 1;
you.skills[SK_FIGHTING] = 1;
you.skills[SK_POISON_MAGIC] = 1;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
you.skills[SK_STABBING] = 2;
you.skills[SK_SPELLCASTING] = 1;
you.skills[SK_ENCHANTMENTS] = 1;
break;
case JOB_THIEF:
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_SHORT_SWORD);
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(2, EQ_CLOAK, OBJ_ARMOUR, ARM_CLOAK);
_newgame_make_item(3, EQ_NONE, OBJ_MISSILES, MI_DART, -1, 20);
// Spriggans used to get a rod of striking, but now that anyone
// can get one when playing an Artificer, this is no longer
// necessary. (jpeg)
if (you.species == SP_SPRIGGAN)
you.inv[0].sub_type = WPN_DAGGER;
if (player_genus(GENPC_OGRE) || you.species == SP_TROLL)
you.inv[0].sub_type = WPN_CLUB;
weap_skill = 2;
you.skills[SK_FIGHTING] = 1;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 2;
you.skills[SK_STABBING] = 2;
you.skills[SK_TRAPS_DOORS] = 2;
you.skills[SK_THROWING] = 2;
break;
case JOB_ASSASSIN:
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_DAGGER, -1, 1, 2, 2);
_newgame_make_item(1, EQ_NONE, OBJ_WEAPONS, WPN_BLOWGUN);
_newgame_make_item(2, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_ROBE);
_newgame_make_item(3, EQ_CLOAK, OBJ_ARMOUR, ARM_CLOAK);
_newgame_make_item(4, EQ_NONE, OBJ_MISSILES, MI_NEEDLE, -1, 10);
set_item_ego_type(you.inv[4], OBJ_MISSILES, SPMSL_POISONED);
_newgame_make_item(5, EQ_NONE, OBJ_MISSILES, MI_NEEDLE, -1, 3);
set_item_ego_type(you.inv[5], OBJ_MISSILES, SPMSL_CURARE);
if (you.species == SP_OGRE || you.species == SP_TROLL)
you.inv[0].sub_type = WPN_CLUB;
weap_skill = 2;
you.skills[SK_FIGHTING] = 2;
you.skills[SK_DODGING] = 1;
you.skills[SK_STEALTH] = 3;
you.skills[SK_STABBING] = 2;
you.skills[SK_THROWING] = 2;
break;
case JOB_HUNTER:
// Equipment.
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_DAGGER);
switch (you.species)
{
case SP_MOUNTAIN_DWARF:
case SP_DEEP_DWARF:
case SP_HILL_ORC:
case SP_CENTAUR:
case SP_OGRE:
you.inv[0].sub_type = WPN_HAND_AXE;
break;
case SP_GHOUL:
case SP_TROLL:
_newgame_clear_item(0);
break;
default:
break;
}
switch (you.species)
{
case SP_SLUDGE_ELF:
case SP_HILL_ORC:
case SP_MERFOLK:
_newgame_make_item(1, EQ_NONE, OBJ_MISSILES, MI_JAVELIN, -1, 6, 1);
_newgame_make_item(2, EQ_NONE, OBJ_MISSILES, MI_THROWING_NET, -1,
2);
break;
case SP_TROLL:
case SP_OGRE:
_newgame_make_item(1, EQ_NONE, OBJ_MISSILES, MI_LARGE_ROCK, -1, 5,
1);
_newgame_make_item(2, EQ_NONE, OBJ_MISSILES, MI_THROWING_NET, -1,
3);
break;
case SP_HALFLING:
case SP_KOBOLD:
_newgame_make_item(1, EQ_NONE, OBJ_WEAPONS, WPN_SLING);
_newgame_make_item(2, EQ_NONE, OBJ_MISSILES, MI_SLING_BULLET, -1,
30, 1);
// Give them a buckler, as well; sling users + bucklers is an ideal
// combination.
_newgame_make_item(4, EQ_SHIELD, OBJ_ARMOUR, ARM_BUCKLER);
you.skills[SK_SHIELDS] = 2;
// Wield the sling instead.
you.equip[EQ_WEAPON] = 1;
break;
case SP_MOUNTAIN_DWARF:
case SP_DEEP_DWARF:
_newgame_make_item(1, EQ_NONE, OBJ_WEAPONS, WPN_CROSSBOW);
_newgame_make_item(2, EQ_NONE, OBJ_MISSILES, MI_BOLT, -1, 25, 1);
// Wield the crossbow instead.
you.equip[EQ_WEAPON] = 1;
break;
default:
_newgame_make_item(1, EQ_NONE, OBJ_WEAPONS, WPN_BOW);
_newgame_make_item(2, EQ_NONE, OBJ_MISSILES, MI_ARROW, -1, 25, 1);
// Wield the bow instead.
you.equip[EQ_WEAPON] = 1;
break;
}
_newgame_make_item(3, EQ_BODY_ARMOUR, OBJ_ARMOUR, ARM_LEATHER_ARMOUR,
ARM_ANIMAL_SKIN);
// Skills.
you.skills[SK_FIGHTING] = 2;
you.skills[SK_DODGING] = 2;
you.skills[SK_STEALTH] = 1;
weap_skill = 1;
if (is_range_weapon(you.inv[1]))
you.skills[range_skill(you.inv[1])] = 4;
else
you.skills[SK_THROWING] = 4;
break;
case JOB_WANDERER:
_create_wanderer();
break;
case JOB_ARTIFICER:
// Equipment. Dagger, and armour or robe.
_newgame_make_item(0, EQ_WEAPON, OBJ_WEAPONS, WPN_DAGGER);
_newgame_make_item(1, EQ_BODY_ARMOUR, OBJ_ARMOUR,
ARM_LEATHER_ARMOUR, ARM_ROBE);
// Choice of lesser wands, 15 charges plus wand of random
// effects: confusion, enslavement, slowing, magic dart, frost,
// flame; OR a rod of striking, 8 charges and no random effects.
if (!_choose_wand())
return (false);
// If an offensive wand or the rod of striking was chosen,
// don't hand out a weapon.
if (item_is_rod(you.inv[2]))
{
// If the rod of striking was chosen, put it in the first
// slot and wield it.
you.inv[0] = you.inv[2];
you.equip[EQ_WEAPON] = 0;
_newgame_clear_item(2);
}
else if (you.inv[3].base_type != OBJ_WANDS
|| you.inv[3].sub_type != WAND_CONFUSION
&& you.inv[3].sub_type != WAND_ENSLAVEMENT)
{
_newgame_clear_item(0);
}
// Skills
you.skills[SK_EVOCATIONS] = 4;
you.skills[SK_TRAPS_DOORS] = 3;
you.skills[SK_DODGING] = 2;
you.skills[SK_FIGHTING] = 1;
you.skills[SK_STEALTH] = 1;
break;
default:
break;
}
// Deep Dwarves get healing potions and wand of healing (3).
if (you.species == SP_DEEP_DWARF)
{
_newgame_make_item(-1, EQ_NONE, OBJ_POTIONS, POT_HEALING, -1, 2);
_newgame_make_item(-1, EQ_NONE, OBJ_POTIONS, POT_HEAL_WOUNDS, -1, 2);
_newgame_make_item(-1, EQ_NONE, OBJ_WANDS, WAND_HEALING, -1, 1, 3);
}
if (weap_skill)
{
if (!you.weapon())
you.skills[SK_UNARMED_COMBAT] = weap_skill;
else
you.skills[weapon_skill(*you.weapon())] = weap_skill;
}
init_skill_order();
if (you.religion != GOD_NO_GOD)
{
you.worshipped[you.religion] = 1;
set_god_ability_slots();
}
return (true);
}