diff options
author | haranp <haranp@c06c8d41-db1a-0410-9941-cceddc491573> | 2009-02-04 14:32:24 +0000 |
---|---|---|
committer | haranp <haranp@c06c8d41-db1a-0410-9941-cceddc491573> | 2009-02-04 14:32:24 +0000 |
commit | f5b7079207156bdd82d713a60cf79229fe33a109 (patch) | |
tree | 667500f1eb3c6a5e67eee0a2d997105d934af173 /crawl-ref/source/effects.cc | |
parent | 5be9c59587164c09a41da05a8de9e0d3588412ab (diff) | |
download | crawl-ref-f5b7079207156bdd82d713a60cf79229fe33a109.tar.gz crawl-ref-f5b7079207156bdd82d713a60cf79229fe33a109.zip |
Clean up acquirement subtype determination code. (Another goto bites the dust.)
Be a bit more generous regarding books for Evoc and Invoc types.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@8890 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/effects.cc')
-rw-r--r-- | crawl-ref/source/effects.cc | 1104 |
1 files changed, 537 insertions, 567 deletions
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc index 7b8405868e..58016e05a5 100644 --- a/crawl-ref/source/effects.cc +++ b/crawl-ref/source/effects.cc @@ -863,655 +863,630 @@ static armour_type _random_nonbody_armour_type() return (at); } -static int _find_acquirement_subtype(object_class_type class_wanted, - int &quantity) -{ - ASSERT(class_wanted != OBJ_RANDOM); - - int type_wanted = OBJ_RANDOM; - int iteration = 0; +const int max_has_value = 100; +typedef FixedVector<int, max_has_value> has_vector; - const int max_has_value = 100; - FixedVector< int, max_has_value > already_has; +static armour_type _acquirement_armour_subtype() +{ + // Increasing the representation of the non-body armour + // slots here to make up for the fact that there's one + // one type of item for most of them. -- bwr + // + // NUM_ARMOURS is body armour and handled below + armour_type result = (coinflip()) ? NUM_ARMOURS + : _random_nonbody_armour_type(); + + // Some species specific fitting problems. + // FIXME: switch to the cleaner logic in can_wear_armour() + switch (you.species) + { + case SP_OGRE: + case SP_TROLL: + 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: + case SP_SPRIGGAN: + if (result == ARM_GLOVES + || result == ARM_BOOTS + || result == ARM_CENTAUR_BARDING + || result == ARM_NAGA_BARDING) + { + result = ARM_ROBE; // no heavy armour + } + else if (result == ARM_SHIELD) + { + if (you.species == SP_SPRIGGAN) + result = ARM_BUCKLER; + else if (coinflip()) // giant races: 50/50 shield/large shield + result = ARM_LARGE_SHIELD; + } + else if (result == NUM_ARMOURS) + { + result = ARM_ROBE; // no heavy armour, see below + } + break; - skill_type best_spell = SK_NONE; - skill_type best_any = SK_NONE; + case SP_NAGA: + if (result == ARM_BOOTS || result == ARM_CENTAUR_BARDING) + result = ARM_NAGA_BARDING; + break; - already_has.init(0); + case SP_CENTAUR: + if (result == ARM_BOOTS || result == ARM_NAGA_BARDING) + result = ARM_CENTAUR_BARDING; + break; - int spell_skills = 0; - for (int i = SK_SPELLCASTING; i <= SK_POISON_MAGIC; i++) - spell_skills += you.skills[i]; + default: + if (result == ARM_CENTAUR_BARDING || result == ARM_NAGA_BARDING) + result = ARM_BOOTS; + break; + } - for (int acqc = 0; acqc < ENDOFPACK; acqc++) + // Mutation specific problems (horns allow caps). + if (result == ARM_BOOTS && !player_has_feet() + || result == ARM_GLOVES && you.has_claws(false) >= 3) { - if (is_valid_item( you.inv[acqc] ) - && you.inv[acqc].base_type == class_wanted) - { - ASSERT( you.inv[acqc].sub_type < max_has_value ); - already_has[you.inv[acqc].sub_type] += you.inv[acqc].quantity; - } + result = NUM_ARMOURS; } - if (class_wanted == OBJ_FOOD) + // Do this here, before acquirement()'s call to can_wear_armour(), + // so that caps will be just as common as helmets for those + // that can't wear helmets. + // We check for the mutation directly to avoid acquirement fiddles + // with vampires. + if (result == ARM_HELMET + && (!you_can_wear(EQ_HELMET) || you.mutation[MUT_HORNS])) { - // food is a little less predictable now -- bwr + result = coinflip()? ARM_CAP : ARM_WIZARD_HAT; + } - if (you.species == SP_GHOUL) - { - type_wanted = one_chance_in(10) ? FOOD_ROYAL_JELLY - : FOOD_CHUNK; - } - else if (you.species == SP_VAMPIRE) - { - // Vampires really don't want any OBJ_FOOD but OBJ_CORPSES - // but it's easier to just give them a potion of blood - // class type is set elsewhere - type_wanted = POT_BLOOD; - quantity = 2 + random2(4); - } + // Now we'll randomly pick a body armour (light only in the + // case of ARM_ROBE). Unlike before, now we're only giving + // out the finished products here, never the hides. -- bwr + if (result == NUM_ARMOURS || result == ARM_ROBE) + { + // start with normal base armour + if (result == ARM_ROBE) + result = coinflip() ? ARM_ROBE : ARM_ANIMAL_SKIN; else { - // Meat is better than bread (except for herbivores), and - // by choosing it as the default we don't have to worry - // about special cases for carnivorous races (e.g. kobolds) - type_wanted = FOOD_MEAT_RATION; + result = static_cast<armour_type>(ARM_ROBE + random2(8)); - if (player_mutation_level(MUT_HERBIVOROUS)) - type_wanted = FOOD_BREAD_RATION; + if (one_chance_in(10) && you.skills[SK_ARMOUR] >= 10) + result = ARM_CRYSTAL_PLATE_MAIL; - // If we have some regular rations, then we're probably more - // interested in faster foods (especially royal jelly)... - // otherwise the regular rations should be a good enough offer. - if (already_has[FOOD_MEAT_RATION] - + already_has[FOOD_BREAD_RATION] >= 2 || coinflip()) - { - type_wanted = one_chance_in(5) ? FOOD_HONEYCOMB - : FOOD_ROYAL_JELLY; - } + if (one_chance_in(10)) + result = ARM_ANIMAL_SKIN; } - quantity = 3 + random2(5); - - // giving more of the lower food value items - if (type_wanted == FOOD_HONEYCOMB || type_wanted == FOOD_CHUNK) + // everyone can wear things made from hides + if (one_chance_in(20)) { - quantity += random2avg(10, 2); + result = static_cast<armour_type>( + random_choose_weighted(20, ARM_TROLL_LEATHER_ARMOUR, + 20, ARM_STEAM_DRAGON_ARMOUR, + 15, ARM_MOTTLED_DRAGON_ARMOUR, + 15, ARM_SWAMP_DRAGON_ARMOUR, + 10, ARM_DRAGON_ARMOUR, + 10, ARM_ICE_DRAGON_ARMOUR, + 5, ARM_STORM_DRAGON_ARMOUR, + 5, ARM_GOLD_DRAGON_ARMOUR, + 0)); } } - else if (class_wanted == OBJ_WEAPONS) - { - // Now asking for a weapon is biased towards your skills, - // although launchers are right out for now. -- bwr - int count = 0; - int skill = SK_FIGHTING; - for (int i = SK_SHORT_BLADES; i <= SK_DARTS; i++) - { - if (i == SK_UNUSED_1) - continue; - - // Adding a small constant allows for the occasional - // weapon in an untrained skill. - - const int weight = you.skills[i] + 1; - count += weight; + return (result); +} - if (x_chance_in_y(weight, count)) - skill = i; - } +// Write results into arguments. +void _acquirement_determine_food(int& type_wanted, int& quantity, + const has_vector& already_has) +{ + // food is a little less predictable now -- bwr + if (you.species == SP_GHOUL) + type_wanted = one_chance_in(10) ? FOOD_ROYAL_JELLY : FOOD_CHUNK; + else if (you.species == SP_VAMPIRE) + { + // Vampires really don't want any OBJ_FOOD but OBJ_CORPSES + // but it's easier to just give them a potion of blood + // class type is set elsewhere + type_wanted = POT_BLOOD; + quantity = 2 + random2(4); + } + else + { + // Meat is better than bread (except for herbivores), and + // by choosing it as the default we don't have to worry + // about special cases for carnivorous races (e.g. kobolds) + type_wanted = FOOD_MEAT_RATION; - count = 0; + if (player_mutation_level(MUT_HERBIVOROUS)) + type_wanted = FOOD_BREAD_RATION; - item_def item_considered; - item_considered.base_type = OBJ_WEAPONS; - for (int i = 0; i < NUM_WEAPONS; ++i) + // If we have some regular rations, then we're probably more + // interested in faster foods (especially royal jelly)... + // otherwise the regular rations should be a good enough offer. + if (already_has[FOOD_MEAT_RATION] + + already_has[FOOD_BREAD_RATION] >= 2 || coinflip()) { - item_considered.sub_type = i; + type_wanted = one_chance_in(5) ? FOOD_HONEYCOMB + : FOOD_ROYAL_JELLY; + } + } - const int acqweight = property(item_considered, PWPN_ACQ_WEIGHT); + quantity = 3 + random2(5); - if (!acqweight) - continue; + // giving more of the lower food value items + if (type_wanted == FOOD_HONEYCOMB || type_wanted == FOOD_CHUNK) + { + quantity += random2avg(10, 2); + } +} - int wskill = range_skill(OBJ_WEAPONS, i); - if (wskill == SK_THROWING) - wskill = weapon_skill(OBJ_WEAPONS, i); +static int _acquirement_weapon_subtype() +{ + // Asking for a weapon is biased towards your skills. + // First pick a skill, weighting towards those you have. + int count = 0; + int skill = SK_FIGHTING; - if (wskill == skill && x_chance_in_y(acqweight, count += acqweight)) - type_wanted = i; - } - } - else if (class_wanted == OBJ_MISSILES) + for (int i = SK_SHORT_BLADES; i <= SK_DARTS; i++) { - int count = 0; - int skill = SK_THROWING; + if (i == SK_UNUSED_1) + continue; - for (int i = SK_SLINGS; i <= SK_DARTS; i++) - { - if (you.skills[i]) - { - count += you.skills[i]; - if (x_chance_in_y(you.skills[i], count)) - skill = i; - } - } + // Adding a small constant allows for the occasional + // weapon in an untrained skill. - switch (skill) - { - case SK_SLINGS: - type_wanted = MI_STONE; - break; + const int weight = you.skills[i] + 1; + count += weight; - case SK_BOWS: - type_wanted = MI_ARROW; - break; + if (x_chance_in_y(weight, count)) + skill = i; + } - case SK_CROSSBOWS: - type_wanted = MI_DART; - for (int i = 0; i < ENDOFPACK; i++) - { - // Assuming that crossbow in inventory means that they - // want bolts for it (not darts for a hand crossbow)... - // perhaps we should check for both and compare ammo - // amounts on hand? - if (is_valid_item( you.inv[i] ) - && you.inv[i].base_type == OBJ_WEAPONS - && you.inv[i].sub_type == WPN_CROSSBOW) - { - type_wanted = MI_BOLT; - break; - } - } - break; - case SK_DARTS: - type_wanted = MI_DART; - for (int i = 0; i < ENDOFPACK; i++) - { - if (is_valid_item( you.inv[i] ) - && you.inv[i].base_type == OBJ_WEAPONS - && you.inv[i].sub_type == WPN_BLOWGUN) - { - // Assuming that blowgun in inventory means that they - // may want needles for it (but darts might also be - // wanted). Maybe expand this... see above comment. - if (coinflip()) - type_wanted = MI_NEEDLE; - break; - } - } - break; + // Now choose a subtype which uses that skill. + int result = OBJ_RANDOM; + count = 0; + item_def item_considered; + item_considered.base_type = OBJ_WEAPONS; + for (int i = 0; i < NUM_WEAPONS; ++i) + { + item_considered.sub_type = i; - default: - type_wanted = MI_DART; - break; - } - } - else if (class_wanted == OBJ_ARMOUR) - { - // Increasing the representation of the non-body armour - // slots here to make up for the fact that there's one - // one type of item for most of them. -- bwr - // - // OBJ_RANDOM is body armour and handled below - type_wanted = (coinflip())? OBJ_RANDOM : - static_cast<int>(_random_nonbody_armour_type()); - - // Some species specific fitting problems. - switch (you.species) - { - case SP_OGRE: - case SP_TROLL: - 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: - case SP_SPRIGGAN: - if (type_wanted == ARM_GLOVES || type_wanted == ARM_BOOTS - || type_wanted == ARM_CENTAUR_BARDING - || type_wanted == ARM_NAGA_BARDING) - { - type_wanted = ARM_ROBE; // no heavy armour - } - else if (type_wanted == ARM_SHIELD) - { - if (you.species == SP_SPRIGGAN) - type_wanted = ARM_BUCKLER; - else if (coinflip()) // giant races: 50/50 shield/large shield - type_wanted = ARM_LARGE_SHIELD; - } - else if (type_wanted == OBJ_RANDOM) - { - type_wanted = ARM_ROBE; // no heavy armour, see below - } - break; + const int acqweight = property(item_considered, PWPN_ACQ_WEIGHT); - case SP_NAGA: - if (type_wanted == ARM_BOOTS || type_wanted == ARM_CENTAUR_BARDING) - type_wanted = ARM_NAGA_BARDING; - break; + if (!acqweight) + continue; - case SP_CENTAUR: - if (type_wanted == ARM_BOOTS || type_wanted == ARM_NAGA_BARDING) - type_wanted = ARM_CENTAUR_BARDING; - break; + int wskill = range_skill(OBJ_WEAPONS, i); + if (wskill == SK_THROWING) + wskill = weapon_skill(OBJ_WEAPONS, i); - default: - if (type_wanted == ARM_CENTAUR_BARDING - || type_wanted == ARM_NAGA_BARDING) - { - type_wanted = ARM_BOOTS; - } - break; - } + if (wskill == skill && x_chance_in_y(acqweight, count += acqweight)) + result = i; + } + return (result); +} - // Mutation specific problems (horns allow caps). - if (type_wanted == ARM_BOOTS && !player_has_feet() - || type_wanted == ARM_GLOVES && you.has_claws(false) >= 3) +static bool _have_item_with_types(object_class_type basetype, int subtype) +{ + for (int i = 0; i < ENDOFPACK; i++) + { + const item_def& item = you.inv[i]; + if (is_valid_item(item) + && item.base_type == basetype && item.sub_type == subtype) { - type_wanted = OBJ_RANDOM; + return (true); } + } + return (false); +} + +static missile_type _acquirement_missile_subtype() +{ + int count = 0; + int skill = SK_THROWING; - // Do this here, before acquirement()'s call to can_wear_armour(), - // so that caps will be just as common as helmets for those - // that can't wear helmets. - // We check for the mutation directly to avoid acquirement fiddles - // with vampires. - if (type_wanted == ARM_HELMET - && (!you_can_wear(EQ_HELMET) || you.mutation[MUT_HORNS])) + for (int i = SK_SLINGS; i <= SK_DARTS; i++) + { + if (you.skills[i]) { - type_wanted = coinflip()? ARM_CAP : ARM_WIZARD_HAT; + count += you.skills[i]; + if (x_chance_in_y(you.skills[i], count)) + skill = i; } + } - // Now we'll randomly pick a body armour (light only in the - // case of ARM_ROBE). Unlike before, now we're only giving - // out the finished products here, never the hides. -- bwr - if (type_wanted == OBJ_RANDOM || type_wanted == ARM_ROBE) - { - // start with normal base armour - if (type_wanted == ARM_ROBE) - type_wanted = coinflip() ? ARM_ROBE : ARM_ANIMAL_SKIN; - else - { - type_wanted = ARM_ROBE + random2(8); + missile_type result = MI_DART; - if (one_chance_in(10) && you.skills[SK_ARMOUR] >= 10) - type_wanted = ARM_CRYSTAL_PLATE_MAIL; + switch (skill) + { + case SK_SLINGS: result = MI_STONE; break; + case SK_BOWS: result = MI_ARROW; break; - if (one_chance_in(10)) - type_wanted = ARM_ANIMAL_SKIN; - } + case SK_CROSSBOWS: + // Assuming that crossbow in inventory means that they + // want bolts for it (not darts for a hand crossbow)... + // perhaps we should check for both and compare ammo + // amounts on hand? + result = (_have_item_with_types(OBJ_WEAPONS, WPN_CROSSBOW) ? MI_BOLT + : MI_DART); + break; - // everyone can wear things made from hides - if (one_chance_in(20)) - { - int rnd = random2(20); - - type_wanted = (rnd < 4) ? ARM_TROLL_LEATHER_ARMOUR : // 20% - (rnd < 8) ? ARM_STEAM_DRAGON_ARMOUR : // 20% - (rnd < 11) ? ARM_MOTTLED_DRAGON_ARMOUR : // 15% - (rnd < 14) ? ARM_SWAMP_DRAGON_ARMOUR : // 15% - (rnd < 16) ? ARM_DRAGON_ARMOUR : // 10% - (rnd < 18) ? ARM_ICE_DRAGON_ARMOUR : // 10% - (rnd < 19) ? ARM_STORM_DRAGON_ARMOUR // 5% - : ARM_GOLD_DRAGON_ARMOUR; // 5% - } - } - } - else if (class_wanted != OBJ_GOLD) - { - do - { - unsigned char i; + case SK_DARTS: + // Assuming that blowgun in inventory means that they + // may want needles for it (but darts might also be + // wanted). Maybe expand this... see above comment. + result = + (_have_item_with_types(OBJ_WEAPONS, WPN_BLOWGUN) && coinflip()) + ? MI_NEEDLE : MI_DART; + break; - switch (class_wanted) - { - case OBJ_JEWELLERY: - // Try for a base type the player hasn't identified - for (i = 0; i < 10; i++) - { - type_wanted = random2(24); + default: + break; + } + return (result); +} - if (one_chance_in(3)) - type_wanted = AMU_RAGE + random2(10); +static int _acquirement_jewellery_subtype() +{ + int result = 0; - if (get_ident_type(OBJ_JEWELLERY, type_wanted) == - ID_UNKNOWN_TYPE) - { - break; - } - } - break; + // Try ten times to give something the player hasn't seen. + for (int i = 0; i < 10; i++) + { + // 1/3 amulets, 2/3 rings. + if (one_chance_in(3)) + { + result = AMU_FIRST_AMULET + + random2(NUM_JEWELLERY - AMU_FIRST_AMULET); + } + else + { + result = random2(NUM_RINGS); + } - case OBJ_BOOKS: - // Remember, put rarer books higher in the list. - iteration = 1; - type_wanted = NUM_BOOKS; + // If we haven't seen this yet, we're done. + if (get_ident_type(OBJ_JEWELLERY, result) == ID_UNKNOWN_TYPE) + break; + } - best_spell = best_skill( SK_SPELLCASTING, (NUM_SKILLS - 1), - best_spell ); + return (result); +} - which_book: -#if DEBUG_DIAGNOSTICS - mprf(MSGCH_DIAGNOSTICS, - "acquirement: iteration = %d, best_spell = %d", - iteration, best_spell ); -#endif +static int _choose_first_unseen_book(int first, ...) +{ + va_list args; + va_start(args, first); - switch (best_spell) - { - default: - case SK_SPELLCASTING: - if (you.skills[SK_SPELLCASTING] <= 3 - && !you.had_book[BOOK_CANTRIPS]) - { - // Handful of level one spells, very useful for the - // new spellcaster who's asking for a book -- bwr - type_wanted = BOOK_CANTRIPS; - } - else if (!you.had_book[BOOK_MINOR_MAGIC_I]) - type_wanted = BOOK_MINOR_MAGIC_I + random2(3); - else if (!you.had_book[BOOK_WIZARDRY]) - type_wanted = BOOK_WIZARDRY; - else if (!you.had_book[BOOK_CONTROL]) - type_wanted = BOOK_CONTROL; - else if (!you.had_book[BOOK_POWER]) - type_wanted = BOOK_POWER; - break; + if (first == NUM_BOOKS || !you.had_book[first]) + { + va_end(args); + return (first); + } - case SK_POISON_MAGIC: - if (!you.had_book[BOOK_YOUNG_POISONERS]) - type_wanted = BOOK_YOUNG_POISONERS; - else if (!you.had_book[BOOK_ENVENOMATIONS]) - type_wanted = BOOK_ENVENOMATIONS; - break; + int nargs = 100; - case SK_EARTH_MAGIC: - if (!you.had_book[BOOK_GEOMANCY]) - type_wanted = BOOK_GEOMANCY; - else if (!you.had_book[BOOK_EARTH]) - type_wanted = BOOK_EARTH; - break; + while (nargs-- > 0) + { + const int another = va_arg(args, int); + if (another == NUM_BOOKS || !you.had_book[another]) + { + va_end(args); + return (another); + } + } - case SK_AIR_MAGIC: - // removed the book of clouds... all the other elements - // (and most other spell skills) only get two. - if (!you.had_book[BOOK_AIR]) - type_wanted = BOOK_AIR; - else if (!you.had_book[BOOK_SKY]) - type_wanted = BOOK_SKY; - break; + ASSERT(nargs > 0); - case SK_ICE_MAGIC: - if (!you.had_book[BOOK_FROST]) - type_wanted = BOOK_FROST; - else if (!you.had_book[BOOK_ICE]) - type_wanted = BOOK_ICE; - break; + va_end(args); + return (NUM_BOOKS); +} - case SK_FIRE_MAGIC: - if (!you.had_book[BOOK_FLAMES]) - type_wanted = BOOK_FLAMES; - else if (!you.had_book[BOOK_FIRE]) - type_wanted = BOOK_FIRE; - break; - case SK_SUMMONINGS: - if (!you.had_book[BOOK_CALLINGS]) - type_wanted = BOOK_CALLINGS; - else if (!you.had_book[BOOK_SUMMONINGS]) - type_wanted = BOOK_SUMMONINGS; - // now a Vehumet special -- bwr - // else if (!you.had_book[BOOK_DEMONOLOGY]) - // type_wanted = BOOK_DEMONOLOGY; - break; +static int _acquirement_book_subtype() +{ + int result = NUM_BOOKS; - case SK_ENCHANTMENTS: - best_any = best_skill(SK_FIGHTING, (NUM_SKILLS - 1), 99); - - // So many enchantment books! I really can't feel - // guilty at all for dividing out the fighting - // books and forcing the player to raise a fighting - // skill (or enchantments in the case of Crusaders) - // to get the remaining books... enchantments are - // much too good (most spells, lots of books here, - // id wand charges, gives magic resistance), - // something will eventually have to be done. -- bwr - if (best_any >= SK_FIGHTING && best_any <= SK_STAVES) - { - // Fighter mages get the fighting enchantment books - if (!you.had_book[BOOK_WAR_CHANTS]) - type_wanted = BOOK_WAR_CHANTS; - else if (!you.had_book[BOOK_TUKIMA]) - type_wanted = BOOK_TUKIMA; - } - else if (!you.had_book[BOOK_CHARMS]) - type_wanted = BOOK_CHARMS; - else if (!you.had_book[BOOK_HINDERANCE]) - type_wanted = BOOK_HINDERANCE; - else if (!you.had_book[BOOK_ENCHANTMENTS]) - type_wanted = BOOK_ENCHANTMENTS; - break; + skill_type best_spell_skill = SK_NONE; + // Do two iterations: one with the best spell skill, one with + // the second-best. + for (int i = 0; i < 2; ++i) + { + // FIXME: Assumes that SK_POISON_MAGIC is the last spell skill. + best_spell_skill = best_skill(SK_SPELLCASTING, SK_POISON_MAGIC, + best_spell_skill); + switch (best_spell_skill) + { + default: + case SK_SPELLCASTING: + if (you.skills[SK_SPELLCASTING] <= 3 + && !you.had_book[BOOK_CANTRIPS]) + { + // Handful of level one spells, very useful for the + // new spellcaster who's asking for a book -- bwr + result = BOOK_CANTRIPS; + } + else + { + result = _choose_first_unseen_book(BOOK_MINOR_MAGIC_I, + BOOK_WIZARDRY, + BOOK_CONTROL, + BOOK_POWER, + NUM_BOOKS); + if (result == BOOK_MINOR_MAGIC_I) + result += random2(3); + } + break; - case SK_CONJURATIONS: - if (!you.had_book[BOOK_CONJURATIONS_I]) - type_wanted = give_first_conjuration_book(); - else if (!you.had_book[BOOK_TEMPESTS]) - type_wanted = BOOK_TEMPESTS; + case SK_POISON_MAGIC: + result = _choose_first_unseen_book(BOOK_YOUNG_POISONERS, + BOOK_ENVENOMATIONS, + NUM_BOOKS); + break; - // now a Vehumet special -- bwr - // else if (!you.had_book[BOOK_ANNIHILATIONS]) - // type_wanted = BOOK_ANNIHILATIONS; - break; + case SK_EARTH_MAGIC: + result = _choose_first_unseen_book(BOOK_GEOMANCY, BOOK_EARTH, + NUM_BOOKS); + break; - case SK_NECROMANCY: - if (!you.had_book[BOOK_NECROMANCY]) - type_wanted = BOOK_NECROMANCY; - else if (!you.had_book[BOOK_DEATH]) - type_wanted = BOOK_DEATH; - else if (!you.had_book[BOOK_UNLIFE]) - type_wanted = BOOK_UNLIFE; - - // now a Kikubaaqudgha special -- bwr - // else if (!you.had_book[BOOK_NECRONOMICON]) - // type_wanted = BOOK_NECRONOMICON; - break; + case SK_AIR_MAGIC: + result = _choose_first_unseen_book(BOOK_AIR, BOOK_SKY, NUM_BOOKS); + break; - case SK_TRANSLOCATIONS: - if (!you.had_book[BOOK_SPATIAL_TRANSLOCATIONS]) - type_wanted = BOOK_SPATIAL_TRANSLOCATIONS; - else if (!you.had_book[BOOK_WARP]) - type_wanted = BOOK_WARP; - break; + case SK_ICE_MAGIC: + result = _choose_first_unseen_book(BOOK_FROST, BOOK_ICE, NUM_BOOKS); + break; - case SK_TRANSMUTATION: - if (!you.had_book[BOOK_CHANGES]) - type_wanted = BOOK_CHANGES; - else if (!you.had_book[BOOK_TRANSFIGURATIONS]) - type_wanted = BOOK_TRANSFIGURATIONS; - else if (!you.had_book[BOOK_MUTATIONS]) - type_wanted = BOOK_MUTATIONS; - break; + case SK_FIRE_MAGIC: + result = _choose_first_unseen_book(BOOK_FLAMES, BOOK_FIRE, + NUM_BOOKS); + break; - case SK_DIVINATIONS: //jmf: added 24mar2000 - if (!you.had_book[BOOK_SURVEYANCES]) - type_wanted = BOOK_SURVEYANCES; - else if (!you.had_book[BOOK_DIVINATIONS]) - type_wanted = BOOK_DIVINATIONS; - break; - } -/* - if (type_wanted == 99 && glof == best_skill(SK_SPELLCASTING, (NUM_SKILLS - 1), 99)) -*/ - if (type_wanted == NUM_BOOKS && iteration == 1) - { - best_spell = best_skill( SK_SPELLCASTING, NUM_SKILLS - 1, - best_skill(SK_SPELLCASTING, - NUM_SKILLS - 1, 99) ); - iteration++; - goto which_book; - } + case SK_SUMMONINGS: + // Don't give Demonology, that's a Vehumet special. + result = _choose_first_unseen_book(BOOK_CALLINGS, BOOK_SUMMONINGS, + NUM_BOOKS); + break; - // If we don't have a book, try and get a new one. - if (type_wanted > MAX_FIXED_BOOK) - { - do - { - type_wanted = random2(NUM_NORMAL_BOOKS); - if (one_chance_in(500)) - break; - } - while (you.had_book[type_wanted]); - } + case SK_ENCHANTMENTS: + { + skill_type best_any = best_skill(SK_FIGHTING, (NUM_SKILLS - 1)); - // If the book is invalid find any valid one. - while (type_wanted == BOOK_HEALING) - type_wanted = random2(NUM_NORMAL_BOOKS); - break; + // So many enchantment books! I really can't feel + // guilty at all for dividing out the fighting + // books and forcing the player to raise a fighting + // skill (or enchantments in the case of Crusaders) + // to get the remaining books... enchantments are + // much too good (most spells, lots of books here, + // id wand charges, gives magic resistance), + // something will eventually have to be done. -- bwr + if (best_any >= SK_FIGHTING && best_any <= SK_STAVES) + { + // Fighter mages get the fighting enchantment books + result = _choose_first_unseen_book(BOOK_WAR_CHANTS, BOOK_TUKIMA, + NUM_BOOKS); + } + else + { + result = _choose_first_unseen_book(BOOK_CHARMS, BOOK_HINDERANCE, + BOOK_ENCHANTMENTS, + NUM_BOOKS); + } + break; + } - case OBJ_STAVES: - type_wanted = random2(13); + case SK_CONJURATIONS: + // Don't give Annihilations, that's a Vehumet special. + result = _choose_first_unseen_book(BOOK_CONJURATIONS_I, + BOOK_TEMPESTS, NUM_BOOKS); + if (result == BOOK_CONJURATIONS_I) + result = give_first_conjuration_book(); + break; - if (type_wanted >= 10) - type_wanted = STAFF_AIR + type_wanted - 10; + case SK_NECROMANCY: + // Don't give the Necromicon, that's a Kikubaaqudgha special. + result = _choose_first_unseen_book(BOOK_NECROMANCY, BOOK_DEATH, + BOOK_UNLIFE, NUM_BOOKS); + break; - // Elemental preferences -- bwr - if (type_wanted == STAFF_FIRE || type_wanted == STAFF_COLD) - { - if (you.skills[SK_FIRE_MAGIC] > you.skills[SK_ICE_MAGIC]) - type_wanted = STAFF_FIRE; - else if (you.skills[SK_FIRE_MAGIC] != you.skills[SK_ICE_MAGIC]) - type_wanted = STAFF_COLD; - } - else if (type_wanted == STAFF_AIR || type_wanted == STAFF_EARTH) - { - if (you.skills[SK_AIR_MAGIC] > you.skills[SK_EARTH_MAGIC]) - type_wanted = STAFF_AIR; - else if (you.skills[SK_AIR_MAGIC] != you.skills[SK_EARTH_MAGIC]) - type_wanted = STAFF_EARTH; - } + case SK_TRANSLOCATIONS: + result = _choose_first_unseen_book(BOOK_SPATIAL_TRANSLOCATIONS, + BOOK_WARP, NUM_BOOKS); + break; - best_spell = best_skill( SK_SPELLCASTING, (NUM_SKILLS-1), 99 ); + case SK_TRANSMUTATION: + result = _choose_first_unseen_book(BOOK_CHARMS, + BOOK_TRANSFIGURATIONS, + BOOK_MUTATIONS, + NUM_BOOKS); + break; - // If we're going to give out an enhancer staff, - // we should at least bias things towards the - // best spell skill. -- bwr - switch (best_spell) - { - case SK_FIRE_MAGIC: - if (!already_has[STAFF_FIRE]) - type_wanted = STAFF_FIRE; - break; + case SK_DIVINATIONS: + result = _choose_first_unseen_book(BOOK_SURVEYANCES, + BOOK_DIVINATIONS, + NUM_BOOKS); + break; + } - case SK_ICE_MAGIC: - if (!already_has[STAFF_COLD]) - type_wanted = STAFF_COLD; - break; + // If we succeeded the first time around, don't do another try. + if (result != NUM_BOOKS) + break; + } - case SK_AIR_MAGIC: - if (!already_has[STAFF_AIR]) - type_wanted = STAFF_AIR; - break; + // If we don't have a book, try and get a new one. + if (result == NUM_BOOKS) + { + do + { + result = random2(NUM_NORMAL_BOOKS); + if (one_chance_in(500)) + break; + } + while (you.had_book[result]); + } - case SK_EARTH_MAGIC: - if (!already_has[STAFF_EARTH]) - type_wanted = STAFF_EARTH; - break; + // If the book is invalid, find any valid one. + while (result == BOOK_HEALING) + result = random2(NUM_NORMAL_BOOKS); - case SK_POISON_MAGIC: - if (!already_has[STAFF_POISON]) - type_wanted = STAFF_POISON; - break; + return result; +} - case SK_NECROMANCY: - if (!already_has[STAFF_DEATH]) - type_wanted = STAFF_DEATH; - break; +static int _acquirement_staff_subtype(const has_vector& already_has) +{ + int result = random2(STAFF_FIRST_ROD); + + // Elemental preferences -- bwr + if (result == STAFF_FIRE || result == STAFF_COLD) + { + if (you.skills[SK_FIRE_MAGIC] > you.skills[SK_ICE_MAGIC]) + result = STAFF_FIRE; + if (you.skills[SK_FIRE_MAGIC] < you.skills[SK_ICE_MAGIC]) + result = STAFF_COLD; + } + else if (result == STAFF_AIR || result == STAFF_EARTH) + { + if (you.skills[SK_AIR_MAGIC] > you.skills[SK_EARTH_MAGIC]) + result = STAFF_AIR; + if (you.skills[SK_AIR_MAGIC] < you.skills[SK_EARTH_MAGIC]) + result = STAFF_EARTH; + } + + skill_type best_spell_skill = best_skill(SK_SPELLCASTING, NUM_SKILLS - 1); + +#define TRY_GIVE(x) { if (!already_has[x]) result = x; } + // If we're going to give out an enhancer staff, + // we should at least bias things towards the + // best spell skill. -- bwr + switch (best_spell_skill) + { + case SK_FIRE_MAGIC: TRY_GIVE(STAFF_FIRE); break; + case SK_ICE_MAGIC: TRY_GIVE(STAFF_COLD); break; + case SK_AIR_MAGIC: TRY_GIVE(STAFF_AIR); break; + case SK_EARTH_MAGIC: TRY_GIVE(STAFF_EARTH); break; + case SK_POISON_MAGIC: TRY_GIVE(STAFF_POISON); break; + case SK_NECROMANCY: TRY_GIVE(STAFF_DEATH); break; + case SK_CONJURATIONS: TRY_GIVE(STAFF_CONJURATION); break; + case SK_ENCHANTMENTS: TRY_GIVE(STAFF_ENCHANTMENT); break; + case SK_SUMMONINGS: TRY_GIVE(STAFF_SUMMONING); break; +#undef TRY_GIVE + + case SK_EVOCATIONS: + if (!one_chance_in(4)) + result = random_rod_subtype(); + break; - case SK_CONJURATIONS: - if (!already_has[STAFF_CONJURATION]) - type_wanted = STAFF_CONJURATION; - break; + default: // Invocations and leftover spell schools. + switch (random2(5)) + { + case 0: result = STAFF_WIZARDRY; break; + case 1: result = STAFF_POWER; break; + case 2: result = STAFF_ENERGY; break; + case 3: result = STAFF_CHANNELING; break; + case 4: break; // keep the original random staff + } + break; + } - case SK_ENCHANTMENTS: - if (!already_has[STAFF_ENCHANTMENT]) - type_wanted = STAFF_ENCHANTMENT; - break; + int spell_skills = 0; + for (int i = SK_SPELLCASTING; i <= SK_POISON_MAGIC; i++) + spell_skills += you.skills[i]; - case SK_SUMMONINGS: - if (!already_has[STAFF_SUMMONING]) - type_wanted = STAFF_SUMMONING; - break; + // Increased chance of getting a rod for new or + // non-spellcasters. -- bwr + if (one_chance_in(20) + || (spell_skills <= 1 // short on spells + && result < STAFF_FIRST_ROD + && !one_chance_in(4))) + { + result = coinflip() ? STAFF_STRIKING : random_rod_subtype(); + } - case SK_EVOCATIONS: - if (!one_chance_in(4)) - type_wanted = random_rod_subtype(); - break; + return (result); +} - default: // Invocations and leftover spell schools. - switch (random2(5)) - { - case 0: - type_wanted = STAFF_WIZARDRY; - break; +static int _acquirement_misc_subtype() +{ + int result = NUM_MISCELLANY; + do + { + result = random2(NUM_MISCELLANY); + } + while (result == MISC_HORN_OF_GERYON + || result == MISC_RUNE_OF_ZOT + || result == MISC_CRYSTAL_BALL_OF_FIXATION + || result == MISC_EMPTY_EBONY_CASKET + || result == MISC_DECK_OF_PUNISHMENT); - case 1: - type_wanted = STAFF_POWER; - break; + return (result); +} - case 2: - type_wanted = STAFF_ENERGY; - break; +static int _find_acquirement_subtype(object_class_type class_wanted, + int &quantity) +{ + ASSERT(class_wanted != OBJ_RANDOM); - case 3: - type_wanted = STAFF_CHANNELING; - break; + int type_wanted = OBJ_RANDOM; - case 4: - break; - } - break; - } + // Write down what the player is carrying. + has_vector already_has; + already_has.init(0); + for (int i = 0; i < ENDOFPACK; ++i) + { + const item_def& item = you.inv[i]; + if (is_valid_item(item) && item.base_type == class_wanted) + { + ASSERT(item.sub_type < max_has_value); + already_has[item.sub_type] += item.quantity; + } + } - // Increased chance of getting a rod for new or - // non-spellcasters. -- bwr - if (one_chance_in(20) - || (spell_skills <= 1 // short on spells - && type_wanted < STAFF_SMITING - && !one_chance_in(4))) - { - type_wanted = coinflip() ? STAFF_STRIKING : - random_rod_subtype(); - } - break; + bool try_again = (class_wanted == OBJ_JEWELLERY + || class_wanted == OBJ_BOOKS + || class_wanted == OBJ_STAVES + || class_wanted == OBJ_MISCELLANY); + do + { + switch (class_wanted) + { + case OBJ_FOOD: + // set type_wanted and quantity + _acquirement_determine_food(type_wanted, quantity, already_has); + break; - case OBJ_MISCELLANY: - do - { - type_wanted = random2(NUM_MISCELLANY); - } - while (type_wanted == MISC_HORN_OF_GERYON - || type_wanted == MISC_RUNE_OF_ZOT - || type_wanted == MISC_CRYSTAL_BALL_OF_FIXATION - || type_wanted == MISC_EMPTY_EBONY_CASKET - || type_wanted == MISC_DECK_OF_PUNISHMENT); - break; - default: - break; - } + case OBJ_WEAPONS: type_wanted = _acquirement_weapon_subtype(); break; + case OBJ_MISSILES: type_wanted = _acquirement_missile_subtype(); break; + case OBJ_ARMOUR: type_wanted = _acquirement_armour_subtype(); break; + case OBJ_BOOKS: type_wanted = _acquirement_book_subtype(); break; + case OBJ_MISCELLANY: type_wanted = _acquirement_misc_subtype(); break; + case OBJ_STAVES: type_wanted = _acquirement_staff_subtype(already_has); + break; + case OBJ_JEWELLERY: type_wanted = _acquirement_jewellery_subtype(); + break; - ASSERT( type_wanted < max_has_value ); + default: break; // gold } - while (already_has[type_wanted] && !one_chance_in(200)); - } + + if (try_again) + { + ASSERT(type_wanted < max_has_value); + if (!already_has[type_wanted]) + try_again = false; + if (one_chance_in(200)) + try_again = false; + } + } while (try_again); return (type_wanted); } @@ -1818,9 +1793,7 @@ bool acquirement(object_class_type class_wanted, int agent, { case RING_SLAYING: // Make sure plus to damage is >= 1. - thing.plus2 = abs( thing.plus2 ); - if (thing.plus2 == 0) - thing.plus2 = 1; + thing.plus2 = std::max(abs(thing.plus2), 1); // fall through... case RING_PROTECTION: @@ -1829,16 +1802,14 @@ bool acquirement(object_class_type class_wanted, int agent, case RING_DEXTERITY: case RING_EVASION: // Make sure plus is >= 1. - thing.plus = abs( thing.plus ); - if (thing.plus == 0) - thing.plus = 1; + thing.plus = std::max(abs(thing.plus), 1); break; case RING_HUNGER: case AMU_INACCURACY: // These are the only truly bad pieces of jewellery. if (!one_chance_in(9)) - make_item_randart( thing ); + make_item_randart(thing); break; default: @@ -1858,13 +1829,12 @@ bool acquirement(object_class_type class_wanted, int agent, case SP_GHOUL: case SP_VAMPIRE: { - int brand = get_weapon_brand( thing ); + int brand = get_weapon_brand(thing); if (brand == SPWPN_HOLY_WRATH) { - if (!is_random_artefact( thing )) + if (!is_random_artefact(thing)) { - set_item_ego_type( thing, - OBJ_WEAPONS, SPWPN_VORPAL ); + set_item_ego_type(thing, OBJ_WEAPONS, SPWPN_VORPAL); } else { @@ -1872,7 +1842,7 @@ bool acquirement(object_class_type class_wanted, int agent, for (; brand == SPWPN_HOLY_WRATH; brand = get_weapon_brand(thing)) { - make_item_randart( thing ); + make_item_randart(thing); } } } |