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 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;
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 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
* "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.
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.
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.
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.