Allow Shadow Creatures spell (along with anything that uses create_monster(), mons_place(), place_monster() or pick_random_monster()) to work in portal vaults; only fixed ziggurats to use it. Must be done by manually calling dgn.set_random_mon_list(); can't automatically set it from MONS since the actual frequency of the monster specified that way is controlled by the number of 1/2/3/etc symbols that are in the map combined with symbol shufflings and substitutions. Can add an RMONS keyword to handle it if needed.
Limitations: can only use level id or monster type + base type. Monster number (specific number of hydra heads), colour, items, band, and patrolling are all discarded/ignored. This can be improved if it's too limiting.
New per-level env.properties CrawlHashTable stores the information. env.properties can be used to store new per-level data without breaking savefile compatibility (though changing the handling of old data in env.properties can still break compatibility).
Random monster spawn rate is now controlled by the per-level env.spawn_random_rate (though the default rates are all the same as before), in case any weird portal vaults want to turn on random monster spawning. Could also be used to alter the random spawn rate for places like Vault:8
Prevented Shadow Creatues from working in the Temple.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7816 c06c8d41-db1a-0410-9941-cceddc491573
55PFDYPVE6JVGDYPCFUE4XS2523PVSV4CSIFRW6A2GGX4I6VWRWQC
SM6YRPYZS6LMDQA6X3VAOK2PGMUFKPD7JMWJISOQSMX2CBR4ISPAC
5ASC3STDYCNLZFEBN6UTMUCGDETHBR2OCBZCF5VIAZ5RRWLOTDYQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
WF2DSJGR6PKLGQSXEFF4ZW4EZZFGMHXPXWUYAKYBPFJH6KJKAANQC
CGYTZT5QWIEGYKUOLOK7MFXSLJKLYRZONER5ZCDZO5XYWSLG475QC
IVVTHLTTLOP5TSULXJWUSSXHOKYWVU3OWKYVK45A7RIB6V34MYQAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
L4RYVF46EQKMVOEADGRG4WMPVTQ6NNFGYMU4SHAH6XJIKWVHT77QC
7KWDC7XFNMBLSUO2HISIROBINZBX5T67LJEEXTAORXW2YZ7VWFGAC
TLA5UN6LZPXGKERI27EFY4HKIIU3VU5Y7ZU54WXL6ANBUV2VOTMQC
AM7QPHDAWNXHLUEVUHVRHG2RO2DOIEFFU4GV3DCIROW6O5HW7H4AC
ID373JATLMWAY526Q6Q5FXHRNFWMEOFXPHGPAUUY5OAMPFDN5SJAC
Y4NA3JSN63RLATF4NNBPSR5CWF5Z7UEMWCGVX4B6NOAR47CGM4GQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
FEAW5HX4TFYOEGUNSESIV5IB2Z65XEJ2EALW6PYNTPRLTPT7APUAC
HUMRQOA7Y32XBXQNR5LUIWM2ZB57XDW4ZQHMZ4RDGDZATY6K73SQC
BBOGP3OC5GTMXVAETTIJYXNZMPSLJFMXODHJBURLYOHGZFAEV7HQC
YMLVBQ6M27MECUVMU3BQP3WSGR7GW4XJMQIHLGHHWMVXHMMIXOYAC
NKONHW4JNY6HP2M63MNPM3H64ZWSUNUT5FX2STW4KTS4AMXJXXVQC
UZ6N6HOUPGVSPC5NQROEEDWMEGJA5XUWUY2AKH5QG65AZ25PVXDAC
KABMEHHPUS7RLDZ2A5ADC6GMYSBJ5OHD3WPUBDWBM22XWHB7X6WQC
2TECJQA3PK7OYSSTOWZSQFWMTATJRHHA6JV3IGRHFGS3R7U27RZAC
MDFQRJ6QZNFUBVSFWLXUJ6EBXOU47T3CVDI2XKBGNNRF4DXDKESQC
WDEFQ6YABDQIGJXW5KT3OGR3EO6FZHXZELIRVIXQ4XDYTVOV5V6AC
R2DQBWKIW7YUJB5SOQ7J274JIYRVX4H3ISFRPAL5RG2RVVP4G2KAC
K2MLPJIAXXZRWEWZCNSGICCBNIU2WAAPT7SPIMOH7FLLTOB4QFRAC
ADIVVYTV2MJ3XVRYDNBLPTAEACCNF27XZDCRVZFQEHRHPMZGNITQC
7YSKYUNV34XIWRTJUHJV4QMQRTXXYDIXM5AZSPSDPAYDW4B4PU6QC
A3CO4KBFTFU3ZSHWRY2OPPX3MMTFV7OUCZGL7Q4Y2FU7JO4AP7MAC
ZLQAAP55CJ77XIJN3DZVPT4GTTVLIBFJLIJJKI6L5UBSHX7VUK6AC
AUXHSGS4EFOPZ6TVZYWNVOUDO7NYKUKE3HBKGQQWTALSVFOE3HAAC
ED62QWGKBPORWVKDFOQRKJXEIWZVNGR3O4KWQBDSRNPT36AYOQYAC
PRG7UT7G56GT4W3FQ3KG5JRPGMKKJBFDLVHDLYFQK6IZW25JQLBQC
AVCMVFA3MKCXHO6H44UK5KJNIHTGQV7UA7GYXM26VI6TXXU5ZN6QC
KSM4H3SBM6FIQTUEGHXXYATJXEOJ4EKUBAFCRMFKSHY7N2HWECRQC
ASLW3Z5PAVZSWJEMMMVZT226P44EKSAD47QS72JIFJESAI3RPN3AC
52W74WXL5XIH6YFJBQRVAO47YHCS3CPMUUZS4Q3AZ3HAPDWMT54AC
NDTQUANX3GZ6HZP5FONYNJUYPD3R2P6SGRC3ICKJ7ZWF3KO23LTAC
IHV7JHD4E67NEGLZEO3FPQGJPJF3IAV6QV5A63FPG4SU2VRFV47QC
X7MFMKQTNZ2IWBFVGS6WQV7NRNKJ3DWQAW2X7IQMFQQXW24AHPZQC
DUFJKFM5KBCM4272ZKLBPGKHLMDLK6RABUNTDEWRZULTKDTHHSBAC
NCKCO6W5HW7C6OPJG4EVHKQCDX6AL7S4ACORY45STKQTNPMAJ7IQC
/* ***********************************************************************
* called from: luadgn
* *********************************************************************** */
void set_vault_mon_list(const std::vector<mons_spec> &list);
void get_vault_mon_list(std::vector<mons_spec> &list);
/* ***********************************************************************
* called from: files
* *********************************************************************** */
void setup_vault_mon_list();
if (you.level_type == LEVEL_DUNGEON
&& !player_in_branch( BRANCH_ECUMENICAL_TEMPLE )
&& x_chance_in_y(5, (you.char_direction == GDT_DESCENDING) ? 240 : 8))
if (you.level_type == LEVEL_DUNGEON && x_chance_in_y(5, rate))
// No monsters in the Labyrinth, or the Ecumenical Temple, or in Bazaars.
// A portal vault *might* decide to turn on random monster spawning,
// but it's off by default.
if (you.level_type == LEVEL_PORTAL_VAULT && x_chance_in_y(5, rate))
{
mons_place(mgen_data(WANDERING_MONSTER));
viewwindow(true, false);
}
// No random monsters in the Labyrinth.
if (place.level_type == LEVEL_PORTAL_VAULT)
{
monster_type base_type = (monster_type) 0;
coord_def dummy1;
dungeon_char_type dummy2;
monster_type type =
_resolve_monster_type(RANDOM_MONSTER, PROX_ANYWHERE, base_type,
dummy1, 0, &dummy2, &lev_mons);
#if DEBUG || DEBUG_DIAGNOSTICS
if (base_type != 0 && base_type != MONS_PROGRAM_BUG)
mpr("Random portal vault mon discarding base type.",
MSGCH_ERROR);
#endif
return (type);
if (you.level_type == LEVEL_PORTAL_VAULT)
{
if (vault_mon_types.size() == 0)
return (MONS_PROGRAM_BUG);
int i = choose_random_weighted(vault_mon_weights.begin(),
vault_mon_weights.end());
int type = vault_mon_types[i];
int base = vault_mon_bases[i];
if (type == -1)
{
place = level_id::from_packed_place(base);
// If lev_mons is set to you.your_level, it was probably
// set as a default meaning "the current dungeon depth",
// which for a portal vault using it's own definition
// of random monsters means "the depth of whatever place
// we're using for picking the random monster".
if (*lev_mons == you.your_level)
*lev_mons = place.absdepth();
// pick_random_monster() is called below
}
else
{
base_type = (monster_type) base;
mon_type = (monster_type) type;
if (mon_type == RANDOM_DRACONIAN
|| mon_type == RANDOM_BASE_DRACONIAN
|| mon_type == RANDOM_NONBASE_DRACONIAN)
{
mon_type =
_resolve_monster_type(mon_type, proximity,
base_type, pos, mmask,
stair_type, lev_mons);
}
return (mon_type);
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Random monsters for portal vaults.
//
/////////////////////////////////////////////////////////////////////////////
void set_vault_mon_list(const std::vector<mons_spec> &list)
{
CrawlHashTable &props = env.properties;
props.erase(VAULT_MON_TYPES_KEY);
props.erase(VAULT_MON_BASES_KEY);
props.erase(VAULT_MON_WEIGHTS_KEY);
unsigned int size = list.size();
if (size == 0)
{
setup_vault_mon_list();
return;
}
props[VAULT_MON_TYPES_KEY].new_vector(SV_LONG).resize(size);
props[VAULT_MON_BASES_KEY].new_vector(SV_LONG).resize(size);
props[VAULT_MON_WEIGHTS_KEY].new_vector(SV_LONG).resize(size);
CrawlVector &type_vec = props[VAULT_MON_TYPES_KEY];
CrawlVector &base_vec = props[VAULT_MON_BASES_KEY];
CrawlVector &weight_vec = props[VAULT_MON_WEIGHTS_KEY];
for (unsigned int i = 0; i < size; i++)
{
const mons_spec &spec = list[i];
if (spec.place.is_valid())
{
ASSERT(spec.place.level_type != LEVEL_LABYRINTH
&& spec.place.level_type != LEVEL_PORTAL_VAULT);
type_vec[i] = (long) -1;
base_vec[i] = (long) spec.place.packed_place();
}
else
{
ASSERT(spec.mid != RANDOM_MONSTER
&& spec.monbase != RANDOM_MONSTER);
type_vec[i] = (long) spec.mid;
base_vec[i] = (long) spec.monbase;
}
weight_vec[i] = (long) spec.genweight;
}
setup_vault_mon_list();
}
void get_vault_mon_list(std::vector<mons_spec> &list)
{
list.clear();
CrawlHashTable &props = env.properties;
if (!props.exists(VAULT_MON_TYPES_KEY))
return;
ASSERT(props.exists(VAULT_MON_BASES_KEY));
ASSERT(props.exists(VAULT_MON_WEIGHTS_KEY));
CrawlVector &type_vec = props[VAULT_MON_TYPES_KEY];
CrawlVector &base_vec = props[VAULT_MON_BASES_KEY];
CrawlVector &weight_vec = props[VAULT_MON_WEIGHTS_KEY];
ASSERT(type_vec.size() == base_vec.size());
ASSERT(type_vec.size() == weight_vec.size());
unsigned int size = type_vec.size();
for (unsigned int i = 0; i < size; i++)
{
int type = (long) type_vec[i];
int base = (long) base_vec[i];
mons_spec spec;
if (type == -1)
{
spec.place = level_id::from_packed_place(base);
ASSERT(spec.place.is_valid());
ASSERT(spec.place.level_type != LEVEL_LABYRINTH
&& spec.place.level_type != LEVEL_PORTAL_VAULT);
}
else
{
spec.mid = type;
spec.monbase = (monster_type) base;
ASSERT(spec.mid != RANDOM_MONSTER
&& spec.monbase != RANDOM_MONSTER);
}
spec.genweight = (long) weight_vec[i];
list.push_back(spec);
}
}
void setup_vault_mon_list()
{
vault_mon_types.clear();
vault_mon_bases.clear();
vault_mon_weights.clear();
std::vector<mons_spec> list;
get_vault_mon_list(list);
unsigned int size = list.size();
vault_mon_types.resize(size);
vault_mon_bases.resize(size);
vault_mon_weights.resize(size);
for (unsigned int i = 0; i < size; i++)
{
if (list[i].place.is_valid())
{
vault_mon_types[i] = -1;
vault_mon_bases[i] = list[i].place.packed_place();
}
else
{
vault_mon_types[i] = list[i].mid;
vault_mon_bases[i] = list[i].monbase;
}
vault_mon_weights[i] = list[i].genweight;
}
}
result += get_monster_data(type)->name;
switch(type)
{
case RANDOM_MONSTER:
result += "random monster";
return (result);
case RANDOM_DRACONIAN:
result += "random draconian";
return (result);
case RANDOM_BASE_DRACONIAN:
result += "random base draconian";
return (result);
case RANDOM_NONBASE_DRACONIAN:
result += "random nonbase draconian";
return (result);
case WANDERING_MONSTER:
result += "wandering monster";
return (result);
}
const monsterentry *me = get_monster_data(type);
ASSERT(me != NULL);
if (me == NULL)
{
result += make_stringf("invalid type %d", type);
return (result);
}
mons_spec mons_list::get_monster(int slot_index, int list_index) const
{
if (slot_index < 0 || slot_index >= (int) mons.size())
return mons_spec(RANDOM_MONSTER);
const mons_spec_list &list = mons[slot_index].mlist;
if (list_index < 0 || list_index >= (int) list.size())
return mons_spec(RANDOM_MONSTER);
return list[list_index];
}
}
static int dgn_set_random_mon_list(lua_State *ls)
{
if (you.level_type != LEVEL_PORTAL_VAULT)
{
luaL_error(ls, "Can only be used in portal vaults.");
return (0);
}
mons_list mlist = _lua_get_mlist(ls, 1);
if (mlist.size() == 0)
return (0);
if (mlist.size() > 1)
{
luaL_argerror(ls, 1, "Mon list must contain only one slot.");
return (0);
}
const int num_mons = mlist.slot_size(0);
if (num_mons == 0)
{
luaL_argerror(ls, 1, "Mon list is empty.");
return (0);
}
std::vector<mons_spec> mons;
int num_lords = 0;
for (int i = 0; i < num_mons; i++)
{
mons_spec mon = mlist.get_monster(0, i);
// Pandemonium lords are pseudo-unique, so don't randomly generate
// them.
if (mon.mid == MONS_PANDEMONIUM_DEMON)
{
num_lords++;
continue;
}
std::string name;
if (mon.place.is_valid())
{
if (mon.place.level_type == LEVEL_LABYRINTH
|| mon.place.level_type == LEVEL_PORTAL_VAULT)
{
std::string err;
err = make_stringf("mon #%d: Can't use Lab or Portal as a "
"monster place.", i + 1);
luaL_argerror(ls, 1, err.c_str());
return(0);
}
name = mon.place.describe();
}
else
{
if (mon.mid == RANDOM_MONSTER || mon.monbase == RANDOM_MONSTER)
{
std::string err;
err = make_stringf("mon #%d: can't use random monster in "
"list specifying random monsters", i + 1);
luaL_argerror(ls, 1, err.c_str());
return(0);
}
name = mons_type_name(mon.mid, DESC_PLAIN);
}
mons.push_back(mon);
if (mon.number != 0)
mprf(MSGCH_ERROR, "dgn.set_random_mon_list() : number for %s "
"being discarded.",
name.c_str());
if (mon.band)
mprf(MSGCH_ERROR, "dgn.set_random_mon_list() : band request for "
"%s being ignored.",
name.c_str());
if (mon.colour != BLACK)
mprf(MSGCH_ERROR, "dgn.set_random_mon_list() : colour for "
"%s being ignored.",
name.c_str());
if (mon.items.size() > 0)
mprf(MSGCH_ERROR, "dgn.set_random_mon_list() : items for "
"%s being ignored.",
name.c_str());
} // for (int i = 0; i < num_mons; i++)
if (mons.size() == 0 && num_lords > 0)
{
luaL_argerror(ls, 1, "Mon list contains only pandemonium lords.");
return (0);
}
set_vault_mon_list(mons);
return (0);
double elapsed_time; // used during level load
// Place to associate arbitrary data with a particular level.
// Sort of like player::atribute
CrawlHashTable properties;
// Rate at which random monsters spawn, with lower numbers making
// them spawn more often (5 or less causes one to spawn about every
// 5 turns). Set to 0 to stop random generation.
int spawn_random_rate;
// Set default random monster generation rate (smaller is more often,
// except that 0 == no random monsters).
if (you.level_type == LEVEL_DUNGEON)
{
if (you.where_are_you == BRANCH_ECUMENICAL_TEMPLE)
env.spawn_random_rate = 0;
else
env.spawn_random_rate = 240;
}
else if (you.level_type == LEVEL_ABYSS
|| you.level_type == LEVEL_PANDEMONIUM)
{
// Abyss spawn rate is set for those characters that start out in the
// Abyss; otherwise the number is ignored in the Abyss.
env.spawn_random_rate = 50;
}
else
// No random monsters in Labyrinths and portal vaualts.
env.spawn_random_rate = 0;