diff options
-rw-r--r-- | crawl-ref/docs/level-design.txt | 64 | ||||
-rw-r--r-- | crawl-ref/source/debug.cc | 4 | ||||
-rw-r--r-- | crawl-ref/source/dungeon.cc | 111 | ||||
-rw-r--r-- | crawl-ref/source/dungeon.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/itemprop.h | 6 | ||||
-rw-r--r-- | crawl-ref/source/makeitem.cc | 75 | ||||
-rw-r--r-- | crawl-ref/source/makeitem.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.cc | 250 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.h | 128 |
9 files changed, 532 insertions, 110 deletions
diff --git a/crawl-ref/docs/level-design.txt b/crawl-ref/docs/level-design.txt index 5571bec243..1224618aca 100644 --- a/crawl-ref/docs/level-design.txt +++ b/crawl-ref/docs/level-design.txt @@ -455,18 +455,44 @@ ITEM: (list of items, separated by comma) Modifiers: * "q:N" sets the item quantity to N (if N > 0). Does nothing if the item is not stackable. - * "good_item" makes the builder try to make the item a good one. - * "any" by itself gives random choice; you can combine "any" with + * "no_uniq" prevents the item from being turned into an artefact. + * "good_item" makes the builder try to make the item a good one + (acquirement quality). + * "level:N" sets the object's item level (can't be used with + "good_item"). If set to -2 then the object's item level will + be the same as a "*" symbol item (five plus twice the + vault's level number). + * "any" by itself gives a random choice; you can combine "any" with "good_item." * "any book", "any misc" etc. gives a random item of that class. Valid item class names are: gold, weapon, missile, armour, wand, food, scroll, jewelry, potion, book, staff, orb, misc, carrion. All of these are usable in map definitions, apart from "orb" and "carrion". - - Limitations: You can't specify curse status nor item race, - nor can you give specific egos, nor can give fixedarts. You - also can't lay down corpses, skeletons, or chunks. + * "race:race_name", where "race_name" is "elvish", "dwarven" + or "orcish"; it can also be "none" or "no_race" to prevent + the item from being randomly being made racial. Has no effect + if the item can't take that kind of racial setting. + NOTE: Can result in a non-racial item if used with "any" and + the chosen item isn't compatible with the desired race. + * "ego:ego_name", where "ego_name" is something like + "running", "fire_resistance", and so on; "none" can be used + to prevent the item from getting an ego. The item must + be fully specified, so trying "any weapon ego:vorpal" or + "any armour ego:positive_energy" will result in an error. + Trying to give an ego to something which can't accept an + ego will also result in an error. + + WARNING: While checks are done to make sure that an armour + ego isn't given to a weapon, a weapon ego to a missile, + and so on, and also to make sure that egos are only given + to amrours, weapons and missiles, no other checking is + done. Thus it is possible to create a demonic weapon of + holy wrath or a helmet of running. + + Limitations: You can't specify curse status, specificy + pluses or number of charges, force a randart or give fixedarts. + You also can't lay down corpses, skeletons, or chunks. MONS: (list of monsters) These are used to help place specific monsters at specific places @@ -480,14 +506,36 @@ MONS: (list of monsters) Individual monsters may be prefixed with the "generate_awake" (without the quotes). Use this sparingly: - MONS: generate_awake giant beetle + MONS: generate_awake giant beetle Monsters can also be given colours that override their default colour. Use this *very* sparingly: - MONS: col:darkgrey fungus + MONS: col:darkgrey fungus Note that 8, 9, 0 also place monsters (see the table). + A monster can be given specific items by following the monster + name with a semi-colon and then with an item list as described + in ITEM:, but with slashes replaced with pipes and commas replaced + with periods. For example: + MONS: orc ; katana | quick blade . chain mail | scale mail + + will generate an orc wielding either a katana or a quick blade + and wearing either a chain mail or a scale mail. Randarts are + never generated, and ego items are only generated if the ego + is explicitly stated. Note that any items that the monster was + originally generated with will be removed and destroyed. This + can be used force a monster to have no items whatsoever: + MONS: orc; nothing + + Items given to an orc or an elf will be made orcish or elven + unless the item's race type is explicitly set otherwise. + + Limitations: If an item in the item list has alternatives, + there's no way to force all monsters dervied from that monster + spec to choose the same alternative. If a monster is given + a random launcher, there is no way to force the ammo type to + match the launcher type. COLOUR: . = green / blue:5 / red / none COLOUR: allows you to attach explicit colours to any feature. diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc index 6f771a37eb..9536ecba33 100644 --- a/crawl-ref/source/debug.cc +++ b/crawl-ref/source/debug.cc @@ -1,4 +1,4 @@ -/* + /* * File: debug.cc * Summary: Debug and wizard related functions. * Written by: Linley Henzell and Jesse Jones @@ -427,7 +427,7 @@ void create_spec_monster_name(int x, int y) if (y == -1) y = you.y_pos; - const mons_spec mspec = mlist.get_monster(0); + mons_spec mspec = mlist.get_monster(0); if (!force_place && mspec.mid != -1) { coord_def place = find_newmons_square(mspec.mid, x, y); diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc index f90c49cb35..f5212cc142 100644 --- a/crawl-ref/source/dungeon.cc +++ b/crawl-ref/source/dungeon.cc @@ -4173,7 +4173,7 @@ static void dgn_place_item_explicit(const item_spec &spec, const int item_made = items( spec.allow_uniques, spec.base_type, spec.sub_type, true, - level, spec.race ); + level, spec.race, 0, spec.ego ); if (item_made != NON_ITEM && item_made != -1) { @@ -4212,7 +4212,99 @@ static void dgn_place_item_explicit(int index, int x, int y, dgn_place_item_explicit(spec, x, y, level); } -bool dgn_place_monster(const mons_spec &mspec, +static void dgn_give_mon_spec_items(mons_spec &mspec, + const int mindex, + const int mid, + const int monster_level) +{ + monsters &mon(menv[mindex]); + + unwind_var<int> save_speedinc(mon.speed_increment); + + // Get rid of existing equipment. + for (int i = 0; i < NUM_MONSTER_SLOTS; i++) + { + if (mon.inv[i] != NON_ITEM) + { + item_def &item(mitm[mon.inv[i]]); + mon.unequip(item, i, 0, true); + destroy_item(mon.inv[i], true); + mon.inv[i] = NON_ITEM; + } + } + + item_make_species_type racial = MAKE_ITEM_RANDOM_RACE; + + if (mons_genus(mid) == MONS_ORC) + racial = MAKE_ITEM_ORCISH; + else if (mons_genus(mid) == MONS_ELF) + racial = MAKE_ITEM_ELVEN; + + item_list &list = mspec.items; + + const int size = list.size(); + for (int i = 0; i < size; ++i) + { + item_spec spec = list.get_item(i); + + if (spec.base_type == OBJ_UNASSIGNED) + continue; + + // Don't give monster a randart, and don't radnomly give + // monster an ego item. + if (spec.base_type == OBJ_ARMOUR || spec.base_type == OBJ_WEAPONS + || spec.base_type == OBJ_MISSILES) + { + spec.allow_uniques = 0; + if (spec.ego == 0) + spec.ego = SP_FORBID_EGO; + } + + // Gives orcs and elves appropriate racial gear, unless + // otherwise specified. + if (spec.race == MAKE_ITEM_RANDOM_RACE) + { + // But don't automatically give elves elven boots or + // elven cloaks. + if (racial != MAKE_ITEM_ELVEN || spec.base_type != OBJ_ARMOUR + || (spec.sub_type != ARM_CLOAK + && spec.sub_type != ARM_BOOTS)) + { + spec.race = racial; + } + } + + int item_level = monster_level; + + if (spec.level >= 0) + item_level = spec.level; + else + { + switch(spec.level) + { + case ISPEC_GOOD: + item_level = 5 + item_level * 2; + break; + case ISPEC_SUPERB: + item_level = MAKE_GOOD_ITEM; + break; + } + } + + const int item_made = + items( spec.allow_uniques, spec.base_type, spec.sub_type, true, + item_level, spec.race, 0, spec.ego ); + + if (item_made != NON_ITEM && item_made != -1) + { + item_def &item(mitm[item_made]); + mon.pickup_item(item, 0, true); + } + } +} + + +bool dgn_place_monster(mons_spec &mspec, int monster_level, int vx, int vy, bool generate_awake) { @@ -4248,10 +4340,13 @@ bool dgn_place_monster(const mons_spec &mspec, m_generate_awake? BEH_WANDER : BEH_SLEEP, MHITNOT, true, vx, vy, false, PROX_ANYWHERE, mspec.monnum); - if (placed && mindex != -1 && mindex != NON_MONSTER - && mspec.colour != BLACK) + if (placed && mindex != -1 && mindex != NON_MONSTER) { - menv[mindex].colour = mspec.colour; + if (mspec.colour != BLACK) + menv[mindex].colour = mspec.colour; + + if (mspec.items.size() > 0) + dgn_give_mon_spec_items(mspec, mindex, mid, monster_level); } return (placed); } @@ -4260,7 +4355,7 @@ bool dgn_place_monster(const mons_spec &mspec, static bool dgn_place_monster( const vault_placement &place, - const mons_spec &mspec, + mons_spec &mspec, int monster_level, int vx, int vy) { @@ -4278,8 +4373,8 @@ static bool dgn_place_one_monster( { for (int i = 0, size = mons.size(); i < size; ++i) { - if (dgn_place_monster(place, mons.get_monster(i), - monster_level, vx, vy)) + mons_spec spec = mons.get_monster(i); + if (dgn_place_monster(place, spec, monster_level, vx, vy)) { return (true); } diff --git a/crawl-ref/source/dungeon.h b/crawl-ref/source/dungeon.h index c6ee2a3b6f..1d9c8c1e17 100644 --- a/crawl-ref/source/dungeon.h +++ b/crawl-ref/source/dungeon.h @@ -319,7 +319,7 @@ coord_def dgn_find_nearby_stair(dungeon_feature_type stair_to_find, coord_def base_pos, bool find_closest); class mons_spec; -bool dgn_place_monster(const mons_spec &mspec, +bool dgn_place_monster(mons_spec &mspec, int monster_level, int vx, int vy, bool generate_awake); diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h index 6873ac5ab3..433b6ed135 100644 --- a/crawl-ref/source/itemprop.h +++ b/crawl-ref/source/itemprop.h @@ -73,8 +73,12 @@ enum boot_type // used in pluses2 NUM_BOOT_TYPES }; +const int SP_FORBID_EGO = -1; +const int SP_FORBID_BRAND = -1; + enum brand_type // equivalent to (you.inv[].special or mitm[].special) % 30 { + SPWPN_FORBID_BRAND = -1, // -1 SPWPN_NORMAL, // 0 SPWPN_FLAMING, SPWPN_FREEZING, @@ -319,6 +323,7 @@ enum scroll_type enum special_armour_type { + SPARM_FORBID_EGO = -1, // -1 SPARM_NORMAL, // 0 SPARM_RUNNING, SPARM_FIRE_RESISTANCE, @@ -342,6 +347,7 @@ enum special_armour_type enum special_missile_type // to separate from weapons in general {dlb} { + SPMSL_FORBID_BRAND = -1, // -1 SPMSL_NORMAL, // 0 SPMSL_FLAME, // 1 SPMSL_ICE, // 2 diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc index 899ae153aa..a790e647aa 100644 --- a/crawl-ref/source/makeitem.cc +++ b/crawl-ref/source/makeitem.cc @@ -1143,6 +1143,10 @@ static void weapon_add_racial_modifiers(item_def& item) static brand_type determine_weapon_brand(const item_def& item, int item_level) { + // Forced ego. + if (item.special != 0) + return static_cast<brand_type>(item.special); + const bool force_good = (item_level == MAKE_GOOD_ITEM); const int tries = force_good ? 5 : 1; brand_type rc = SPWPN_NORMAL; @@ -1498,6 +1502,11 @@ static void generate_weapon_item(item_def& item, bool allow_uniques, // Artefacts handled, let's make a normal item. const bool force_good = (item_level == MAKE_GOOD_ITEM); + const bool forced_ego = item.special > 0; + const bool no_brand = item.special == SPWPN_FORBID_BRAND; + + if (no_brand) + set_item_ego_type( item, OBJ_WEAPONS, SPWPN_NORMAL ); // If it's forced to be a good item, upgrade the worst weapons. if (force_good @@ -1509,14 +1518,13 @@ static void generate_weapon_item(item_def& item, bool allow_uniques, item.plus = 0; item.plus2 = 0; - item.special = SPWPN_NORMAL; set_equip_race(item, determine_weapon_race(item, item_race)); // if we allow acquirement-type items to be orcish, then // there's a good chance that we'll just strip them of // their ego type at the bottom of this function. -- bwr - if (force_good && get_equip_race( item ) == ISFLAG_ORCISH) + if (force_good && !forced_ego && get_equip_race( item ) == ISFLAG_ORCISH) set_equip_race( item, ISFLAG_NO_RACE ); // Demonic items can't be racial. @@ -1525,15 +1533,17 @@ static void generate_weapon_item(item_def& item, bool allow_uniques, weapon_add_racial_modifiers(item); - if ((force_good || is_demonic(item) || random2(200) <= 50 + item_level) + if ((force_good || is_demonic(item) || forced_ego + || random2(200) <= 50 + item_level) // nobody would bother enchanting a club && item.sub_type != WPN_CLUB && item.sub_type != WPN_GIANT_CLUB && item.sub_type != WPN_GIANT_SPIKED_CLUB) { // Make a better item (possibly ego) - set_item_ego_type(item, OBJ_WEAPONS, - determine_weapon_brand(item, item_level)); + if (!no_brand) + set_item_ego_type(item, OBJ_WEAPONS, + determine_weapon_brand(item, item_level)); // if acquired item still not ego... enchant it up a bit. if (force_good && item.special == SPWPN_NORMAL) @@ -1574,13 +1584,14 @@ static void generate_weapon_item(item_def& item, bool allow_uniques, } } - if (get_equip_race(item) == ISFLAG_ORCISH) + if (get_equip_race(item) == ISFLAG_ORCISH + && !(item_race == MAKE_ITEM_ORCISH && forced_ego)) { // no holy wrath or slay orc and 1/2 the time no-ego const int brand = get_weapon_brand( item ); if (brand == SPWPN_HOLY_WRATH || brand == SPWPN_ORC_SLAYING - || (brand != SPWPN_NORMAL && coinflip())) + || (brand != SPWPN_NORMAL && !forced_ego && coinflip())) { set_item_ego_type( item, OBJ_WEAPONS, SPWPN_NORMAL ); } @@ -1655,6 +1666,10 @@ static item_status_flag_type determine_missile_race(const item_def& item, static special_missile_type determine_missile_brand(const item_def& item, int item_level) { + // Forced ego. + if (item.special != 0) + return static_cast<special_missile_type>(item.special); + const bool force_good = (item_level == MAKE_GOOD_ITEM); special_missile_type rc = SPMSL_NORMAL; // note that needles can only be poisoned @@ -1707,8 +1722,11 @@ static special_missile_type determine_missile_brand(const item_def& item, static void generate_missile_item(item_def& item, int force_type, int item_level, int item_race) { + const bool no_brand = item.special == SPMSL_FORBID_BRAND; + if (no_brand) + item.special = SPMSL_NORMAL; + item.plus = 0; - item.special = SPMSL_NORMAL; if (force_type != OBJ_RANDOM) item.sub_type = force_type; @@ -1735,8 +1753,9 @@ static void generate_missile_item(item_def& item, int force_type, } set_equip_race(item, determine_missile_race(item, item_race)); - set_item_ego_type( item, OBJ_MISSILES, - determine_missile_brand(item, item_level) ); + if (!no_brand) + set_item_ego_type( item, OBJ_MISSILES, + determine_missile_brand(item, item_level) ); // reduced quantity if special if (item.sub_type == MI_JAVELIN || @@ -1915,6 +1934,9 @@ static item_status_flag_type determine_armour_race(const item_def& item, static special_armour_type determine_armour_ego(const item_def& item, int force_type, int item_level) { + if (item.special != 0) + return static_cast<special_armour_type>(item.special); + special_armour_type rc = SPARM_NORMAL; switch (item.sub_type) { @@ -2043,8 +2065,13 @@ static void generate_armour_item(item_def& item, bool allow_uniques, item.plus++; const bool force_good = (item_level == MAKE_GOOD_ITEM); + const bool forced_ego = item.special > 0; + const bool no_ego = item.special == SPARM_FORBID_EGO; + + if (no_ego) + item.special = SPARM_NORMAL; - if (force_good || item.sub_type == ARM_WIZARD_HAT + if (force_good || forced_ego || item.sub_type == ARM_WIZARD_HAT || 50 + item_level >= random2(250)) { // Make a good item... @@ -2053,8 +2080,8 @@ static void generate_armour_item(item_def& item, bool allow_uniques, if (item.sub_type <= ARM_PLATE_MAIL && 20 + item_level >= random2(300)) item.plus += random2(3); - if (30 + item_level >= random2(350) - && (force_good + if (!no_ego && (30 + item_level >= random2(350)) + && (force_good || forced_ego || (!get_equip_race(item) == ISFLAG_ORCISH || (item.sub_type <= ARM_PLATE_MAIL && coinflip())))) { @@ -2102,7 +2129,8 @@ static void generate_armour_item(item_def& item, bool allow_uniques, && item.sub_type <= ARM_SWAMP_DRAGON_ARMOUR) { set_equip_race( item, ISFLAG_NO_RACE ); - set_item_ego_type( item, OBJ_ARMOUR, SPARM_NORMAL ); + if (!forced_ego) + set_item_ego_type( item, OBJ_ARMOUR, SPARM_NORMAL ); } } @@ -2615,9 +2643,18 @@ int items( int allow_uniques, // not just true-false, int item_level, // level of the item, can differ from global int item_race, // weapon / armour racial categories // item_race also gives type of rune! - unsigned mapmask) + unsigned mapmask, + int force_ego) // desired ego/brand { - const bool force_good = (item_level == MAKE_GOOD_ITEM); + // TODO: Allow a combination of force_ego > 0 and + // force_type == OBJ_RANDOM, so that (for example) you could have + // force_class = OBJ_WEAPON, force_type = OBJ_RANDOM and + // force_ego = SPWPN_VORPAL, and a random weapon of a type + // appropriate for the vorpal brand will be chosen. + ASSERT(force_ego <= 0 || + ((force_class == OBJ_WEAPONS || force_class == OBJ_ARMOUR + || force_class == OBJ_MISSILES) + && force_type != OBJ_RANDOM)); // find an empty slot for the item (with culling if required) int p = get_item_slot(10); @@ -2626,6 +2663,12 @@ int items( int allow_uniques, // not just true-false, item_def& item(mitm[p]); + const bool force_good = (item_level == MAKE_GOOD_ITEM); + + if (force_ego > 0) + allow_uniques = false; + item.special = force_ego; + // cap item_level unless an acquirement-level item {dlb}: if (item_level > 50 && !force_good) item_level = 50; diff --git a/crawl-ref/source/makeitem.h b/crawl-ref/source/makeitem.h index 0427be6923..45125d9a0d 100644 --- a/crawl-ref/source/makeitem.h +++ b/crawl-ref/source/makeitem.h @@ -23,7 +23,7 @@ enum item_make_species_type int items( int allow_uniques, object_class_type force_class, int force_type, bool dont_place, int item_level, int item_race, - unsigned mapmask = 0 ); + unsigned mapmask = 0, int force_ego = 0 ); void item_colour( item_def &item ); void init_rod_mp(item_def &item); diff --git a/crawl-ref/source/mapdef.cc b/crawl-ref/source/mapdef.cc index 91243e8c2e..29ad03c881 100644 --- a/crawl-ref/source/mapdef.cc +++ b/crawl-ref/source/mapdef.cc @@ -1905,16 +1905,50 @@ mons_list::mons_spec_slot mons_list::parse_mons_spec(std::string spec) for (int i = 0, ssize = specs.size(); i < ssize; ++i) { std::string s = specs[i]; + std::vector<std::string> parts = split_string(";", s); + std::string mon_str = parts[0]; + mons_spec mspec; - mspec.genweight = find_weight(s); + if (parts.size() > 2) + { + error = make_stringf("Too many semi-colons for '%s' spec.", + mon_str.c_str()); + return (slot); + } + else if (parts.size() == 2) + { + // TODO: Allow for a "fix_slot" type tag which will cause + // all monsters generated from this spec to have the + // exact same equipment. + std::string items_str = parts[1]; + items_str = replace_all(items_str, "|", "/"); + + std::vector<std::string> segs = split_string(".", items_str); + + if (segs.size() > NUM_MONSTER_SLOTS) + { + error = make_stringf("More items than monster item slots " + "for '%s'.", mon_str.c_str()); + return (slot); + } + + for (int j = 0, isize = segs.size(); j < isize; ++j) + { + error = mspec.items.add_item(segs[j], false); + if (!error.empty()) + return (slot); + } + } + + mspec.genweight = find_weight(mon_str); if (mspec.genweight == TAG_UNFOUND || mspec.genweight <= 0) mspec.genweight = 10; - mspec.fix_mons = strip_tag(s, "fix_mons"); - mspec.generate_awake = strip_tag(s, "generate_awake"); + mspec.fix_mons = strip_tag(mon_str, "fix_mons"); + mspec.generate_awake = strip_tag(mon_str, "generate_awake"); - std::string colour = strip_tag_prefix(s, "col:"); + std::string colour = strip_tag_prefix(mon_str, "col:"); if (!colour.empty()) { mspec.colour = str_to_colour(colour, BLACK); @@ -1926,27 +1960,44 @@ mons_list::mons_spec_slot mons_list::parse_mons_spec(std::string spec) } } - trim_string(s); + trim_string(mon_str); - if (s == "8") + if (mon_str == "8") mspec.mlevel = -8; - else if (s == "9") + else if (mon_str == "9") mspec.mlevel = -9; - else if (check_mimic(s, &mspec.mid, &mspec.fix_mons)) + else if (check_mimic(mon_str, &mspec.mid, &mspec.fix_mons)) ; - else if (s != "0") + else if (mon_str != "0") { - const mons_spec nspec = mons_by_name(s); + const mons_spec nspec = mons_by_name(mon_str); if (nspec.mid == MONS_PROGRAM_BUG) { - error = make_stringf("unrecognised monster \"%s\"", s.c_str()); + error = make_stringf("unrecognised monster \"%s\"", + mon_str.c_str()); return (slot); } mspec.mid = nspec.mid; mspec.monnum = nspec.monnum; } + + if (mspec.items.size() > 0) + { + if (mspec.mid == RANDOM_MONSTER) + { + error = "Can't give spec items to a random monster."; + return (slot); + }; + + if (mons_itemuse(mspec.mid) < MONUSE_STARTING_EQUIPMENT) + { + error = make_stringf("Monster '%s' can't use items.", + mon_str.c_str()); + } + } + slot.mlist.push_back(mspec); } @@ -2143,7 +2194,7 @@ item_spec item_list::pick_item(item_spec_slot &slot) return (spec); } - + item_spec item_list::get_item(int index) { if (index < 0 || index >= (int) items.size()) @@ -2189,6 +2240,115 @@ std::string item_list::set_item(int index, const std::string &spec) return (error); } +// TODO: More checking for innapropriate combinations, like the holy +// wrath brand on a demonic weapon or the running ego on a helmet. +static int str_to_ego(item_spec &spec, std::string ego_str) +{ + const char* armour_egos[] = { + "running", + "fire_resistance", + "cold_resistance", + "poison_resistance", + "see_invisible", + "darkness", + "strength", + "dexterity", + "intelligence", + "ponderousness", + "levitation", + "magic_resistance", + "protection", + "stealth", + "resistance", + "positive_energy", + "archmagi", + "preservation", + NULL + }; + + const char* weapon_brands[] = { + "flaming", + "freezing", + "holy_wrath", + "electrocution", + "orc_slaying", + "venom", + "protection", + "draining", + "speed", + "vorpal", + "flame", + "frost", + "vampiricism", + "disruption", + "pain", + "distortion", + "reaching", + "returning", + "confuse", + NULL + }; + + const char* missile_brands[] = { + "flame", + "ice", + "poisoned", + "poisoned_ii", + "curare", + "returning", + NULL + }; + + const char** name_lists[3] = {armour_egos, weapon_brands, missile_brands}; + + int armour_order[3] = {0, 1, 2}; + int weapon_order[3] = {1, 0, 2}; + int missile_order[3] = {2, 0, 1}; + + int *order; + + switch(spec.base_type) + { + case OBJ_ARMOUR: + order = armour_order; + break; + + case OBJ_WEAPONS: + order = weapon_order; + break; + + case OBJ_MISSILES: + order = missile_order; + break; + + default: + DEBUGSTR("Bad base_type for ego'd item."); + return 0; + } + + const char** allowed = name_lists[order[0]]; + + for (int i = 0; allowed[i] != NULL; i++) + { + if (ego_str == allowed[i]) + return (i + 1); + } + + // Incompatible or non-existant ego type + for (int i = 1; i <= 2; i++) + { + const char** list = name_lists[order[i]]; + + for (int j = 0; list[j] != NULL; j++) + if (ego_str == list[j]) + // Ego incompatible with base type. + return (-1); + } + + // Non-existant ego + return 0; +} + item_spec item_list::parse_single_spec(std::string s) { item_spec result; @@ -2210,6 +2370,25 @@ item_spec item_list::parse_single_spec(std::string s) if (qty != TAG_UNFOUND) result.qty = qty; + std::string ego_str = strip_tag_prefix(s, "ego:"); + std::string race_str = strip_tag_prefix(s, "race:"); + lowercase(ego_str); + lowercase(race_str); + + if (race_str == "elven") + result.race = MAKE_ITEM_ELVEN; + else if (race_str == "dwarven") + result.race = MAKE_ITEM_DWARVEN; + else if (race_str == "orcish") + result.race = MAKE_ITEM_ORCISH; + else if (race_str == "none" || race_str == "no_race") + result.race = MAKE_ITEM_NO_RACE; + else if (!race_str.empty()) + { + error = make_stringf("Bad race: %s", race_str.c_str()); + return (result); + } + if (strip_tag(s, "good_item")) result.level = MAKE_GOOD_ITEM; else @@ -2218,7 +2397,10 @@ item_spec item_list::parse_single_spec(std::string s) if (number != TAG_UNFOUND) { if (number <= 0) + { error = make_stringf("Bad item level: %d", number); + return (result); + } result.level = number; } @@ -2234,13 +2416,19 @@ item_spec item_list::parse_single_spec(std::string s) if (uniq != TAG_UNFOUND) { if (uniq <= 0) + { error = make_stringf("Bad uniq level: %d", uniq); + return (result); + } result.allow_uniques = uniq; } } // Clean up after any tag brain damage. trim_string(s); + + if (!ego_str.empty()) + error = "Can't set an ego for random items."; // Completely random? if (s == "random" || s == "any" || s == "%") @@ -2258,6 +2446,9 @@ item_spec item_list::parse_single_spec(std::string s) } else if (s == "$" || s == "gold") { + if (!ego_str.empty()) + error = "Can't set an ego for gold."; + result.base_type = OBJ_GOLD; result.sub_type = OBJ_RANDOM; return (result); @@ -2265,6 +2456,7 @@ item_spec item_list::parse_single_spec(std::string s) if (s == "nothing") { + error.clear(); result.base_type = OBJ_UNASSIGNED; return (result); } @@ -2283,8 +2475,42 @@ item_spec item_list::parse_single_spec(std::string s) } // Check for actual item names. + error.clear(); parse_raw_name(s, result); + if (!error.empty() || ego_str.empty()) + return (result); + + if (result.base_type != OBJ_WEAPONS + && result.base_type != OBJ_MISSILES + && result.base_type != OBJ_ARMOUR) + { + error = "An ego can only be applied to a weapon, missile or " + "armour."; + return (result); + } + + if (ego_str == "none") + { + result.ego = -1; + return (result); + } + + const int ego = str_to_ego(result, ego_str); + + if (ego == 0) + { + error = make_stringf("No such ego as: %s", ego_str.c_str()); + return (result); + } + else if (ego == -1) + { + error = make_stringf("Ego '%s' is incompatible with item '%s'.", + ego_str.c_str(), s.c_str()); + return (result); + } + result.ego = ego; + return (result); } diff --git a/crawl-ref/source/mapdef.h b/crawl-ref/source/mapdef.h index 451e088e93..fe06561cd0 100644 --- a/crawl-ref/source/mapdef.h +++ b/crawl-ref/source/mapdef.h @@ -325,6 +325,69 @@ private: bool solid_checked; }; +enum item_spec_type +{ + ISPEC_GOOD = -2, + ISPEC_SUPERB = -3 +}; + +struct item_spec +{ + int genweight; + + object_class_type base_type; + int sub_type; + int ego; + int allow_uniques; + int level; + int race; + int qty; + + item_spec() : genweight(10), base_type(OBJ_RANDOM), sub_type(OBJ_RANDOM), + ego(0), allow_uniques(1), level(-1), race(MAKE_ITEM_RANDOM_RACE), + qty(0) + { + } +}; +typedef std::vector<item_spec> item_spec_list; + +class item_list +{ +public: + item_list() : items() { } + + void clear(); + + item_spec get_item(int index); + size_t size() const { return items.size(); } + + std::string add_item(const std::string &spec, bool fix = false); + std::string set_item(int index, const std::string &spec); + +private: + struct item_spec_slot + { + item_spec_list ilist; + bool fix_slot; + + item_spec_slot() : ilist(), fix_slot(false) + { + } + }; + +private: + item_spec item_by_specifier(const std::string &spec); + item_spec_slot parse_item_spec(std::string spec); + item_spec parse_single_spec(std::string s); + void parse_raw_name(std::string name, item_spec &spec); + void parse_random_by_class(std::string c, item_spec &spec); + item_spec pick_item(item_spec_slot &slot); + +private: + std::vector<item_spec_slot> items; + std::string error; +}; + struct mons_spec { int mid; @@ -335,11 +398,13 @@ struct mons_spec bool generate_awake; int colour; + item_list items; + mons_spec(int id = RANDOM_MONSTER, int num = 250, int gw = 10, int ml = 0, bool _fixmons = false, bool awaken = false) : mid(id), monnum(num), genweight(gw), mlevel(ml), fix_mons(_fixmons), - generate_awake(awaken), colour(BLACK) + generate_awake(awaken), colour(BLACK), items() { } }; @@ -394,67 +459,6 @@ private: std::string error; }; -enum item_spec_type -{ - ISPEC_GOOD = -2, - ISPEC_SUPERB = -3 -}; - -struct item_spec -{ - int genweight; - - object_class_type base_type; - int sub_type; - int allow_uniques; - int level; - int race; - int qty; - - item_spec() : genweight(10), base_type(OBJ_RANDOM), sub_type(OBJ_RANDOM), - allow_uniques(1), level(-1), race(MAKE_ITEM_RANDOM_RACE), qty(0) - { - } -}; -typedef std::vector<item_spec> item_spec_list; - -class item_list -{ -public: - item_list() : items() { } - - void clear(); - - item_spec get_item(int index); - size_t size() const { return items.size(); } - - std::string add_item(const std::string &spec, bool fix = false); - std::string set_item(int index, const std::string &spec); - -private: - struct item_spec_slot - { - item_spec_list ilist; - bool fix_slot; - - item_spec_slot() : ilist(), fix_slot(false) - { - } - }; - -private: - item_spec item_by_specifier(const std::string &spec); - item_spec_slot parse_item_spec(std::string spec); - item_spec parse_single_spec(std::string s); - void parse_raw_name(std::string name, item_spec &spec); - void parse_random_by_class(std::string c, item_spec &spec); - item_spec pick_item(item_spec_slot &slot); - -private: - std::vector<item_spec_slot> items; - std::string error; -}; - struct feature_spec { int genweight; |