mapdef/vault monsters can now be given an explicit list of items.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@3055 c06c8d41-db1a-0410-9941-cceddc491573
EH4VJW3I5Y4V6DT3YMLNDA3NW2DEAV4LRE4T5IEXAVB4WB3JJMGAC MXOCLQAUGWLOS7AOTYZ46JZDMRL4EVRK5YN4JJUQ76GLKBOBHEVAC MNYDF64QY6NHYKOAFOGBQJFYU7TZDILXRV23EXJPN4IZOCLSJ2AQC MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC APGCKU4AFOV7Z7XIEO5A27H4IFUGDU227I3Z7OIRROYSLOFFBJ5AC WW6THKR7JN447YC23YYHYYNH7ABMCFFSECNUFTIJBZX6JHX6W7TAC 7YSKYUNV34XIWRTJUHJV4QMQRTXXYDIXM5AZSPSDPAYDW4B4PU6QC A3CO4KBFTFU3ZSHWRY2OPPX3MMTFV7OUCZGL7Q4Y2FU7JO4AP7MAC U7BN4TQ36FIOAGBVWQ4A6VXFZN2GETLGCLD4E3MCBA7OQ3TXYUXQC 34C4U6EQWERY75GZJKUCM5KVGU2OUICETS5LGZF6RMKMZT4R5SQAC 5KJCHLIUFKRPMIVWUAYT6EOF7SW4PTQF6Y5OPEFWXGLE7DUGYLZAC DHK4J2ZAMNKLRDX3V3LPBV2REQXY5BV6LX6TLX6ZWHWGPRXWHNZQC JDM27QE4HR52AYFSQE763BFF57ANOTF5MXKMO377PP5EXMN7SAOAC W52PCSHX72WAMWKG6L4BPUBVMO6E72KYYBNKAA7554KNOTY6V7WQC GRH4XPIYHDOXXF3R3QPMZTFHGLO2OJAZS4FLNBBXG3DHTQQM7RDQC OAPAH3WEFTT2T7NVSSENRR5JCIZYA6UZSQQ6LQEHAAXCX6FIM7HQC ANBVGN4RZOMY5LI4QSHOV2477FN55H353ZYLSVCPTXC7AWWSQZBAC 5P64LHKJKGKIO3FUV63KFQ2OHZ5RNRV7WXS25OHXVNYYFZAVGLMAC DTO3EUKWHZ5RJNGNCFYXSOVTIPVXPP637F2W7WFGYKJ7JK7VNKNQC C22455VGUQOSUX2OORA32LROFQ7NNYDMD2ZDTTUZSAQLXK4AD6QAC Q3DNEB5OOJ34P5ML4CMK3L6SCP7RLW7DDOZEG24KZBX3C7BJRQDAC SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC 3C2VE43SHCSBY4LTRTFYFLIPRWFUN6DXU6D34QVWDQTSNRBUFG7AC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC ILN2K6ASDZSMEHOPJ22IZLZJUO6DDGZTKAKXM3YXG6JZZHJNLX4AC R6XS2HO5QX2FJUGL5UQQRNETKCMYWTUFPHPPS5SYWK3OQA4UDUQQC };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;
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;
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);}
}// 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 typefor (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 egoreturn 0;
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 (!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;
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));
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;}
bool dgn_place_monster(const mons_spec &mspec,
// 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,
* "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 willbe the same as a "*" symbol item (five plus twice thevault's level number).* "any" by itself gives a random choice; you can combine "any" with
* "race:race_name", where "race_name" is "elvish", "dwarven"or "orcish"; it can also be "none" or "no_race" to preventthe item from being randomly being made racial. Has no effectif the item can't take that kind of racial setting.NOTE: Can result in a non-racial item if used with "any" andthe 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 usedto prevent the item from getting an ego. The item mustbe 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 anego will also result in an error.
Limitations: You can't specify curse status nor item race,nor can you give specific egos, nor can give fixedarts. Youalso can't lay down corpses, skeletons, or chunks.
WARNING: While checks are done to make sure that an armourego isn't given to a weapon, a weapon ego to a missile,and so on, and also to make sure that egos are only givento amrours, weapons and missiles, no other checking isdone. Thus it is possible to create a demonic weapon ofholy wrath or a helmet of running.Limitations: You can't specify curse status, specificypluses or number of charges, force a randart or give fixedarts.You also can't lay down corpses, skeletons, or chunks.
will generate an orc wielding either a katana or a quick bladeand wearing either a chain mail or a scale mail. Randarts arenever generated, and ego items are only generated if the egois explicitly stated. Note that any items that the monster wasoriginally generated with will be removed and destroyed. Thiscan be used force a monster to have no items whatsoever:MONS: orc; nothingItems given to an orc or an elf will be made orcish or elvenunless 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 monsterspec to choose the same alternative. If a monster is givena random launcher, there is no way to force the ammo type tomatch the launcher type.