monsters, terrain (named altars, traps, shops) and items all on the same square.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1028 c06c8d41-db1a-0410-9941-cceddc491573
JDM27QE4HR52AYFSQE763BFF57ANOTF5MXKMO377PP5EXMN7SAOAC
7VVRO5HMNNOXVRLBLJUCHUJP6MDIRMC2BWCO7MWH4OLQRM3LMTMQC
JQFQX7IWSJ4TYWVUVXAFMCPSAN67PRMNECDQI5WMON2JFMQVVUEQC
JR2RAQ523LOWNDYJNK6AZVKI6WVMI622PIV72XWOVZYPXPUKSQWAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
34C4U6EQWERY75GZJKUCM5KVGU2OUICETS5LGZF6RMKMZT4R5SQAC
WKTZHLOJ65WSK6FR5MF7RWGSMZ22T2D6LHB66FV3IPGXIBLYHHNAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
ZP2KE7A2LE7Z2S7AC45WE4CXDSEVDTWIMV2EM4IBUKXYJIDU6R7QC
A3CO4KBFTFU3ZSHWRY2OPPX3MMTFV7OUCZGL7Q4Y2FU7JO4AP7MAC
MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC
5E7K2S4F4QDZC5UH3JVMYQ5SFJIKIATZII4EJL7IOT66ZBJ5GUPQC
KCHX2F3JFEWOZT3WMJVZAAQUU2QSZ5Q7RDCD7WUJ7VE65J52JFUQC
5P64LHKJKGKIO3FUV63KFQ2OHZ5RNRV7WXS25OHXVNYYFZAVGLMAC
7YSKYUNV34XIWRTJUHJV4QMQRTXXYDIXM5AZSPSDPAYDW4B4PU6QC
KRN7O2VJTLPT5KLUEMNGNYDSVHB3LYWONSREU2GOUXRMM3J6REYQC
C52GDIYN6W4WNYL2QV7L7OKFBVLPFSBQAEBCX44PAXCZ5H7JXCJQC
SVUM62ARSXH6RUBFRWS6KAQC7PTNTMGSV2GPZJQQJ4GNEML2HBVQC
LY6WZIBS6CVHJQJGFPTFGA74PGXM4MJ7GDDPGRK73E4RCXOVNUIQC
kmons : KMONS { }
| KMONS STRING
{
std::string err = lc_map.add_key_mons($2);
if (!err.empty())
yyerror(
make_stringf("Bad arg to KMONS: '%s' (%s)",
$2, err.c_str()).c_str());
}
kitem : KITEM { }
| KITEM STRING
{
std::string err = lc_map.add_key_item($2);
if (!err.empty())
yyerror(
make_stringf("Bad arg to KITEM: '%s' (%s)",
$2, err.c_str()).c_str());
}
}
}
static const char *shop_types[] = {
"weapon",
"armour",
"antique weapon",
"antique armour",
"antiques",
"jewellery",
"wand",
"book",
"food",
"distillery",
"scroll",
"general"
};
int str_to_shoptype(const std::string &s)
{
if (s == "random")
return (SHOP_RANDOM);
for (unsigned i = 0; i < sizeof(shop_types) / sizeof (*shop_types); ++i)
{
if (s == shop_types[i])
return (i);
};
struct feature_spec
{
int genweight;
int feat;
int shop;
int trap;
int glyph;
feature_spec(int f, int wt = 10)
: genweight(wt), feat(f), shop(-1),
trap(-1), glyph(-1)
{ }
feature_spec() : genweight(0), feat(0), shop(-1), trap(-1), glyph(-1) { }
};
typedef std::vector<feature_spec> feature_spec_list;
struct feature_slot
{
feature_spec_list feats;
bool fix_slot;
feature_slot();
feature_spec get_feat();
};
struct keyed_mapspec
{
public:
feature_slot feat;
item_list item;
mons_list mons;
public:
keyed_mapspec();
std::string set_feat(const std::string &s, bool fix);
std::string set_mons(const std::string &s, bool fix);
std::string set_item(const std::string &s, bool fix);
feature_spec get_feat();
mons_spec get_mons();
item_spec get_item();
private:
std::string err;
private:
void parse_features(const std::string &);
feature_spec_list parse_feature(const std::string &s);
feature_spec parse_shop(std::string s, int weight);
feature_spec parse_trap(std::string s, int weight);
}
static std::string split_key_item(const std::string &s,
int *key,
int *separator,
std::string *arg)
{
std::string::size_type
norm = s.find("="),
fixe = s.find(":");
const std::string::size_type sep = norm < fixe? norm : fixe;
if (sep == std::string::npos)
return ("malformed declaration - must use = or :");
std::string what_to_subst = trimmed_string(s.substr(0, sep));
std::string substitute = trimmed_string(s.substr(sep + 1));
if (what_to_subst.length() != 1)
return make_stringf("selector '%s' must be exactly one character",
what_to_subst.c_str());
*key = what_to_subst[0];
*arg = substitute;
*separator = s[sep];
return ("");
std::string::size_type
norm = s.find("="),
fixe = s.find(":");
const std::string::size_type sep = norm < fixe? norm : fixe;
if (sep == std::string::npos)
return ("malformed SUBST declaration - must use = or :");
int sep = 0;
int key = 0;
std::string substitute;
const bool fixed = (sep == fixe);
std::string what_to_subst = trimmed_string(sub.substr(0, sep));
std::string substitute = trimmed_string(sub.substr(sep + 1));
if (what_to_subst.length() != 1)
return make_stringf("selector '%s' must be exactly one character",
what_to_subst.c_str());
std::string err = split_key_item(sub, &key, &sep, &substitute);
if (!err.empty())
return (err);
keyed_mapspec *map_def::mapspec_for_key(int key)
{
keyed_specs::iterator i = keyspecs.find(key);
return i != keyspecs.end()? &i->second : NULL;
}
std::string map_def::add_key_field(
const std::string &s,
std::string (keyed_mapspec::*set_field)(const std::string &s, bool fixed))
{
int key = 0;
int separator = 0;
std::string arg;
std::string err = split_key_item(s, &key, &separator, &arg);
if (!err.empty())
return (err);
keyed_mapspec &km = keyspecs[key];
return ((km.*set_field)(arg, separator == ':'));
}
std::string map_def::add_key_item(const std::string &s)
{
return add_key_field(s, &keyed_mapspec::set_item);
}
std::string map_def::add_key_feat(const std::string &s)
{
return add_key_field(s, &keyed_mapspec::set_feat);
}
std::string map_def::add_key_mons(const std::string &s)
{
return add_key_field(s, &keyed_mapspec::set_mons);
}
//////////////////////////////////////////////////////////////////////////
// keyed_mapspec
keyed_mapspec::keyed_mapspec()
: feat(), item(), mons()
{
}
std::string keyed_mapspec::set_feat(const std::string &s, bool fix)
{
err.clear();
parse_features(s);
feat.fix_slot = fix;
return (err);
}
void keyed_mapspec::parse_features(const std::string &s)
{
feat.feats.clear();
std::vector<std::string> specs = split_string("/", s);
for (int i = 0, size = specs.size(); i < size; ++i)
{
const std::string &spec = specs[i];
feature_spec_list feats = parse_feature(spec);
if (!err.empty())
return;
feat.feats.insert( feat.feats.end(),
feats.begin(),
feats.end() );
}
}
feature_spec keyed_mapspec::parse_trap(std::string s, int weight)
{
strip_tag(s, "trap");
trim_string(s);
lowercase(s);
const int trap = str_to_trap(s);
if (trap == -1)
err = make_stringf("bad trap name: '%s'", s.c_str());
feature_spec fspec(-1, weight);
fspec.trap = trap;
return (fspec);
}
feature_spec keyed_mapspec::parse_shop(std::string s, int weight)
{
strip_tag(s, "shop");
trim_string(s);
lowercase(s);
const int shop = str_to_shoptype(s);
if (shop == -1)
err = make_stringf("bad shop type: '%s'", s.c_str());
feature_spec fspec(-1, weight);
fspec.shop = shop;
return (fspec);
}
feature_spec_list keyed_mapspec::parse_feature(const std::string &str)
{
std::string s = str;
int weight = strip_number_tag(s, "weight:");
if (weight == TAG_UNFOUND || weight <= 0)
weight = 10;
trim_string(s);
feature_spec_list list;
if (s.length() == 1)
{
feature_spec fsp(-1, weight);
fsp.glyph = s[0];
list.push_back( fsp );
return (list);
}
if (s.find("trap") != std::string::npos)
{
list.push_back( parse_trap(s, weight) );
return (list);
}
if (s.find("shop") != std::string::npos
|| s.find("store") != std::string::npos)
{
list.push_back( parse_shop(s, weight) );
return (list);
}
std::vector<dungeon_feature_type> feats = features_by_desc(s);
for (int i = 0, size = feats.size(); i < size; ++i)
list.push_back( feature_spec(feats[i], weight) );
if (feats.empty())
err = make_stringf("no features matching \"%s\"",
str.c_str());
return (list);
}
std::string keyed_mapspec::set_mons(const std::string &s, bool fix)
{
err.clear();
mons.clear();
return (mons.add_mons(s, fix));
}
std::string keyed_mapspec::set_item(const std::string &s, bool fix)
{
err.clear();
item.clear();
return (item.add_item(s, fix));
}
feature_spec keyed_mapspec::get_feat()
{
return feat.get_feat();
}
mons_spec keyed_mapspec::get_mons()
{
return (mons.size()? mons.get_monster(0) : mons_spec(-1));
}
item_spec keyed_mapspec::get_item()
{
if (item.size())
return item.get_item(0);
item_spec spec;
spec.base_type = OBJ_UNASSIGNED;
return (spec);
}
//////////////////////////////////////////////////////////////////////////
// feature_slot
feature_slot::feature_slot() : feats(), fix_slot(false)
{
}
feature_spec feature_slot::get_feat()
{
int tweight = 0;
feature_spec chosen_feat = feature_spec(DNGN_FLOOR);
for (int i = 0, size = feats.size(); i < size; ++i)
{
const feature_spec &feat = feats[i];
if (random2(tweight += feat.genweight) < feat.genweight)
chosen_feat = feat;
}
if (fix_slot)
{
feats.clear();
feats.push_back( chosen_feat );
}
return (chosen_feat);
}
static void dngn_place_item_explicit(const item_spec &spec,
int x, int y, int level)
{
// Dummy object?
if (spec.base_type == OBJ_UNASSIGNED)
return;
if (spec.level >= 0)
level = spec.level;
const int item_made =
items( spec.allow_uniques, spec.base_type, spec.sub_type, true,
level, spec.race );
if (item_made != NON_ITEM && item_made != -1)
{
mitm[item_made].x = x;
mitm[item_made].y = y;
}
}
// Dummy object?
if (spec.base_type == OBJ_UNASSIGNED)
return;
if (spec.level >= 0)
level = spec.level;
const int item_made =
items( spec.allow_uniques, spec.base_type, spec.sub_type, true,
level, spec.race );
if (item_made != NON_ITEM)
static void dngn_make_monster(
const mons_spec &monster_type_thing,
int monster_level,
int vx, int vy)
{
if (monster_type_thing.mid != -1)
mitm[item_made].x = x;
mitm[item_made].y = y;
const int mid = monster_type_thing.mid;
int not_used;
if (mid != RANDOM_MONSTER && mid < NUM_MONSTERS)
{
const int habitat = monster_habitat(mid);
if (habitat != DNGN_FLOOR)
grd[vx][vy] = habitat;
}
place_monster( not_used, mid, monster_level,
monster_type_thing.generate_awake?
BEH_WANDER : BEH_SLEEP,
MHITNOT, true, vx, vy, false );
keyed_mapspec *mapsp = following? NULL : place.map.mapspec_for_key(vgrid);
if (mapsp)
{
const feature_spec f = mapsp->get_feat();
if (f.feat >= 0)
{
grd[vx][vy] = f.feat;
vgrid = -1;
}
else if (f.glyph >= 0)
{
altar_count = vault_grid( place, level_number, vx, vy,
altar_count, acq_item_class,
f.glyph, targets, num_runes,
rune_subst, true );
}
else if (f.shop >= 0)
place_spec_shop(level_number, vx, vy, f.shop);
else if (f.trap >= 0)
{
const int trap =
f.trap == TRAP_INDEPTH? random_trap_for_level(level_number)
: f.trap;
place_specific_trap(vx, vy, trap);
}
else
grd[vx][vy] = DNGN_FLOOR;
if (monster_type_thing.mid != -1)
{
const int mid = monster_type_thing.mid;
if (mid != RANDOM_MONSTER && mid < NUM_MONSTERS)
{
const int habitat = monster_habitat(mid);
if (habitat != DNGN_FLOOR)
grd[vx][vy] = habitat;
}
place_monster( not_used, mid, monster_level,
monster_type_thing.generate_awake?
BEH_WANDER : BEH_SLEEP,
MHITNOT, true, vx, vy, false );
}
dngn_make_monster(monster_type_thing, monster_level,
vx, vy);
}
int str_to_trap(const std::string &s)
{
ASSERT(NUM_TRAPS == sizeof(trap_names) / sizeof(*trap_names));
if (s == "random")
return (TRAP_RANDOM);
else if (s == "suitable")
return (TRAP_INDEPTH);
for (int i = 0; i < NUM_TRAPS; ++i)
{
if (trap_names[i] == s)
return (i);
}
return (-1);
for (unsigned i = 0; i < sizeof(shop_types) / sizeof (*shop_types); ++i)
{
if (strstr(requested_shop, shop_types[i]))
{
new_shop_type = i;
break;
}
}
std::string s = replace_all_of(requested_shop, "*", "");
new_shop_type = str_to_shoptype(s);
#
# KFEAT:
# -----
# The KFEAT: directive allows you to specify a placeholder symbol that is
# replaced with another symbol, named feature, trap, or shop. Eg.:
#
# KFEAT: Z = C / needle trap / antique armour shop / altar of Zin
#
# Replaces occurrences of Z with C (random altar), a needle trap, an
# antique armour shop, or an altar of Zin. Different instances of Z may
# receive different replacements. To force a single replacement for all Z,
# use:
#
# KFEAT: Z : C / needle trap / antique armour shop
#
# You'll notice that 'Z' is the symbol of the Orb of Zot. Kxxx directives
# allow you to assign arbitrary definitions to any symbol.
#
# The placeholder used by KFEAT can be shared by KITEM and KMONS. See below.
# If the placeholder is shared, all defined Kxxxx operations for the
# placeholder are performed.
#
# KMONS:
# -----
# KMONS: allows you to specify a placeholder symbol that indicates the
# position of a monster (or monsters).
#
# KMONS: ? = orc priest / deep elf priest
# Using KMONS: allows you to exceed the 7 slot limit for monsters. It is also
# useful if you want to place a monster on a non-floor square (used in
# association with a KFEAT:). Eg:
#
# KFEAT: Z = W
# KMONS: Z = rat
#
# (Places a rat on a shallow water square for all occurrences of Z.)
#
# KITEM:
# -----
# KITEM: places the specified item at all occurrences of the placeholder. It
# can be combined with KFEAT and KMONS for the same placeholder. Eg:
#
# KITEM: ? = potion of healing / potion of restore abilities
#
#
#
# KFEAT:
# -----
# The KFEAT: directive allows you to specify a placeholder symbol that is
# replaced with another symbol, named feature, trap, or shop. Eg.:
#
# KFEAT: Z = C / needle trap / antique armour shop / altar of Zin
#
# Replaces occurrences of Z with C (random altar), a needle trap, an
# antique armour shop, or an altar of Zin. Different instances of Z may
# receive different replacements. To force a single replacement for all Z,
# use:
#
# KFEAT: Z : C / needle trap / antique armour shop
#
# You'll notice that 'Z' is the symbol of the Orb of Zot. Kxxx directives
# allow you to assign arbitrary definitions to any symbol.
#
# The placeholder used by KFEAT can be shared by KITEM and KMONS. See below.
# If the placeholder is shared, all defined Kxxxx operations for the
# placeholder are performed.
#
# KMONS:
# -----
# KMONS: allows you to specify a placeholder symbol that indicates the
# position of a monster (or monsters).
#
# KMONS: ? = orc priest / deep elf priest
#
# Using KMONS: allows you to exceed the 7 slot limit for monsters. It is also
# useful if you want to place a monster on a non-floor square (used in
# association with a KFEAT:). Eg:
#
# KFEAT: Z = W
# KMONS: Z = rat
#
# (Places a rat on a shallow water square for all occurrences of Z.)
#
# KITEM:
# -----
# KITEM: places the specified item at all occurrences of the placeholder. It
# can be combined with KFEAT and KMONS for the same placeholder. Eg:
#
# KITEM: ? = potion of healing / potion of restore abilities
#