summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-12-13 20:19:13 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-12-13 20:19:13 +0000
commitbf516ccc1f22f3449c9dce1c7828d97747f247d2 (patch)
tree96dd532b044dcaec47349a70db8bc91a9694fa8b
parentc4752d24d70640b0e57a7ed227d40ec4e6a617e2 (diff)
downloadcrawl-ref-bf516ccc1f22f3449c9dce1c7828d97747f247d2.tar.gz
crawl-ref-bf516ccc1f22f3449c9dce1c7828d97747f247d2.zip
mapdef/vault items can now have their race type and ego explicitly set.
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
-rw-r--r--crawl-ref/docs/level-design.txt64
-rw-r--r--crawl-ref/source/debug.cc4
-rw-r--r--crawl-ref/source/dungeon.cc111
-rw-r--r--crawl-ref/source/dungeon.h2
-rw-r--r--crawl-ref/source/itemprop.h6
-rw-r--r--crawl-ref/source/makeitem.cc75
-rw-r--r--crawl-ref/source/makeitem.h2
-rw-r--r--crawl-ref/source/mapdef.cc250
-rw-r--r--crawl-ref/source/mapdef.h128
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;