diff options
-rw-r--r-- | crawl-ref/source/dat/clua/ziggurat.lua | 10 | ||||
-rw-r--r-- | crawl-ref/source/dungeon.cc | 23 | ||||
-rw-r--r-- | crawl-ref/source/externs.h | 12 | ||||
-rw-r--r-- | crawl-ref/source/files.cc | 3 | ||||
-rw-r--r-- | crawl-ref/source/luadgn.cc | 104 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.cc | 13 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.h | 4 | ||||
-rw-r--r-- | crawl-ref/source/mon-util.cc | 29 | ||||
-rw-r--r-- | crawl-ref/source/monplace.cc | 247 | ||||
-rw-r--r-- | crawl-ref/source/monplace.h | 12 | ||||
-rw-r--r-- | crawl-ref/source/tags.cc | 9 | ||||
-rw-r--r-- | crawl-ref/source/travel.cc | 11 |
12 files changed, 462 insertions, 15 deletions
diff --git a/crawl-ref/source/dat/clua/ziggurat.lua b/crawl-ref/source/dat/clua/ziggurat.lua index df33ae1aa0..a022da3c27 100644 --- a/crawl-ref/source/dat/clua/ziggurat.lua +++ b/crawl-ref/source/dat/clua/ziggurat.lua @@ -230,7 +230,7 @@ local function monster_creator_fn(arg) end return mcreator(x, y) end - return { fn = mspec } + return { fn = mspec, spec = arg } elseif atyp == "table" then if not arg.cond or arg.cond() then return util.cathash(monster_creator_fn(arg.spec), arg) @@ -273,6 +273,9 @@ mset_if(depth_lt(6), "place:Pan") local drac_creator = dgn.monster_fn("random draconian") local function mons_drac_gen(x, y, nth) + if nth == 1 then + dgn.set_random_mon_list("random draconian") + end set_random_floor_colour() return drac_creator(x, y) end @@ -283,6 +286,7 @@ local pan_critter_fn = dgn.monster_fn("place:Pan") local function mons_panlord_gen(x, y, nth) set_random_floor_colour() if nth == 1 then + dgn.set_random_mon_list("place:Pan") return pan_lord_fn(x, y) else return pan_critter_fn(x, y) @@ -591,6 +595,10 @@ end local function ziggurat_furnish(centre, entry, exit) local monster_generation = choose_monster_set() + if type(monster_generation.spec) == "string" then + dgn.set_random_mon_list(monster_generation.spec) + end + -- If we're going to spawn jellies, do our loot protection thing. if monster_generation.jelly_protect then zig().level.jelly_protect = true diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc index 335aba7330..3a43bfe349 100644 --- a/crawl-ref/source/dungeon.cc +++ b/crawl-ref/source/dungeon.cc @@ -883,6 +883,9 @@ static void _reset_level() dgn_check_connectivity = false; dgn_zones = 0; + // Forget level properties. + env.properties.clear(); + // Blank level with DNGN_ROCK_WALL. grd.init(DNGN_ROCK_WALL); env.grid_colours.init(BLACK); @@ -924,6 +927,26 @@ static void _reset_level() else env.level_flags = 0; + // 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; + env.floor_colour = BLACK; env.rock_colour = BLACK; diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index 4be82a7c62..7cdaad0d20 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -491,6 +491,7 @@ public: } static level_id parse_level_id(const std::string &s) throw (std::string); + static level_id from_packed_place(const unsigned short place); unsigned short packed_place() const; std::string describe(bool long_name = false, bool with_number = true) const; @@ -1685,7 +1686,16 @@ public: FixedVector< monster_type, 20 > mons_alloc; map_markers markers; - 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; + + double elapsed_time; // used during level load // Number of turns the player has spent on this level. int turns_on_level; diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc index 4bdf77b5f1..17aa7aaece 100644 --- a/crawl-ref/source/files.cc +++ b/crawl-ref/source/files.cc @@ -9,6 +9,7 @@ #include "AppHdr.h" #include "delay.h" #include "files.h" +#include "monplace.h" #include "version.h" #include <string.h> @@ -1292,6 +1293,8 @@ bool load( dungeon_feature_type stair_taken, load_mode_type load_mode, setup_environment_effects(); + setup_vault_mon_list(); + // Inform user of level's annotation. if (load_mode != LOAD_VISITOR && !get_level_annotation().empty() diff --git a/crawl-ref/source/luadgn.cc b/crawl-ref/source/luadgn.cc index 79a97ed07b..e4f9096681 100644 --- a/crawl-ref/source/luadgn.cc +++ b/crawl-ref/source/luadgn.cc @@ -28,6 +28,7 @@ #include "maps.h" #include "misc.h" #include "mon-util.h" +#include "monplace.h" #include "spl-util.h" #include "state.h" #include "stuff.h" @@ -601,6 +602,108 @@ static int dgn_change_branch_flags(lua_State *ls) return (1); } +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); +} + static int dgn_chance(lua_State *ls) { MAP(ls, 1, map); @@ -2799,6 +2902,7 @@ static const struct luaL_reg dgn_lib[] = { "items_at", dgn_items_at }, { "change_level_flags", dgn_change_level_flags }, { "change_branch_flags", dgn_change_branch_flags }, + { "set_random_mon_list", dgn_set_random_mon_list }, { "get_floor_colour", dgn_get_floor_colour }, { "get_rock_colour", dgn_get_rock_colour }, { "change_floor_colour", dgn_change_floor_colour }, diff --git a/crawl-ref/source/mapdef.cc b/crawl-ref/source/mapdef.cc index 7266116331..446583ef5c 100644 --- a/crawl-ref/source/mapdef.cc +++ b/crawl-ref/source/mapdef.cc @@ -1990,6 +1990,19 @@ mons_spec mons_list::get_monster(int index) return (pick_monster( mons[index] )); } +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]; +} + void mons_list::clear() { mons.clear(); diff --git a/crawl-ref/source/mapdef.h b/crawl-ref/source/mapdef.h index 8b2a40260e..68d62b5851 100644 --- a/crawl-ref/source/mapdef.h +++ b/crawl-ref/source/mapdef.h @@ -447,12 +447,14 @@ public: void clear(); mons_spec get_monster(int index); + mons_spec get_monster(int slot_index, int list_index) const; // Returns an error string if the monster is unrecognised. std::string add_mons(const std::string &s, bool fix_slot = false); std::string set_mons(int slot, const std::string &s); - size_t size() const { return mons.size(); } + size_t size() const { return mons.size(); } + size_t slot_size(int slot) const { return mons[slot].mlist.size(); } private: typedef std::vector<mons_spec> mons_spec_list; diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index 38be768b2f..f0e7e8ea33 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -2105,7 +2105,34 @@ std::string mons_type_name(int type, description_level_type desc ) } } - 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); + } + + result += me->name; // Vowel fix: Change 'a orc' to 'an orc'. if (result.length() >= 3 diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc index 753095e6b3..a95a26a04f 100644 --- a/crawl-ref/source/monplace.cc +++ b/crawl-ref/source/monplace.cc @@ -29,6 +29,14 @@ #include "traps.h" #include "view.h" +static std::vector<int> vault_mon_types; +static std::vector<int> vault_mon_bases; +static std::vector<int> vault_mon_weights; + +#define VAULT_MON_TYPES_KEY "vault_mon_types" +#define VAULT_MON_BASES_KEY "vault_mon_bases" +#define VAULT_MON_WEIGHTS_KEY "vault_mon_weights" + // NEW place_monster -- note that power should be set to: // 51 for abyss // 52 for pandemonium @@ -41,6 +49,14 @@ #define BIG_BAND 20 +static monster_type _resolve_monster_type(monster_type mon_type, + proximity_type proximity, + monster_type &base_type, + coord_def &pos, + unsigned mmask, + dungeon_char_type *stair_type, + int *lev_mons); + static void _define_zombie( int mid, monster_type ztype, monster_type cs, int power, coord_def pos ); static monster_type _band_member(band_type band, int power); @@ -219,10 +235,19 @@ void spawn_random_monsters() return; } + if (env.spawn_random_rate == 0) + { +#ifdef DEBUG_MON_CREATION + mpr("random monster gen turned off", MSGCH_DIAGNOSTICS); +#endif + return; + } + + const int rate = (you.char_direction == GDT_DESCENDING) ? + env.spawn_random_rate : 8; + // Place normal dungeon monsters, but not in player LOS. - 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)) { #ifdef DEBUG_MON_CREATION mpr("Create wandering monster...", MSGCH_DIAGNOSTICS); @@ -245,7 +270,8 @@ void spawn_random_monsters() // look a bit strange for a place as chaotic as the Abyss. Then again, // the player is unlikely to meet all of them and notice this.) if (you.level_type == LEVEL_ABYSS - && (you.char_direction != GDT_GAME_START || one_chance_in(10))) + && (you.char_direction != GDT_GAME_START + || x_chance_in_y(5, rate))) { mons_place(mgen_data(WANDERING_MONSTER)); viewwindow(true, false); @@ -253,28 +279,58 @@ void spawn_random_monsters() } // Place Pandemonium monsters. - if (you.level_type == LEVEL_PANDEMONIUM && one_chance_in(10)) + if (you.level_type == LEVEL_PANDEMONIUM && x_chance_in_y(5, rate)) { pandemonium_mons(); viewwindow(true, false); + return; + } + + // 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 monsters in the Labyrinth, or the Ecumenical Temple, or in Bazaars. + // No random monsters in the Labyrinth. } monster_type pick_random_monster(const level_id &place) { - int level = place.absdepth(); + int level; + if (place.level_type == LEVEL_PORTAL_VAULT) + level = you.your_level; + else + level = place.absdepth(); return pick_random_monster(place, level, level); } monster_type pick_random_monster(const level_id &place, int power, int &lev_mons) { - if (place.level_type == LEVEL_LABYRINTH - || place.level_type == LEVEL_PORTAL_VAULT) - { + if (place.level_type == LEVEL_LABYRINTH) + return (MONS_PROGRAM_BUG); + + if (place == BRANCH_ECUMENICAL_TEMPLE) return (MONS_PROGRAM_BUG); + + 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); } monster_type mon_type = MONS_PROGRAM_BUG; @@ -395,7 +451,7 @@ bool drac_colour_incompatible(int drac, int colour) static monster_type _resolve_monster_type(monster_type mon_type, proximity_type proximity, - monster_type base_type, + monster_type &base_type, coord_def &pos, unsigned mmask, dungeon_char_type *stair_type, @@ -495,6 +551,44 @@ static monster_type _resolve_monster_type(monster_type mon_type, mon_type = MONS_DANCING_WEAPON; else { + 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); + } + } // Now pick a monster of the given branch and level. mon_type = pick_random_monster(place, *lev_mons, *lev_mons); } @@ -2836,3 +2930,134 @@ void monster_pathfind::update_pos(coord_def npos, int total) add_new_pos(npos, total); } + +///////////////////////////////////////////////////////////////////////////// +// +// 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; + } +} diff --git a/crawl-ref/source/monplace.h b/crawl-ref/source/monplace.h index 2b4529e59f..24be04f149 100644 --- a/crawl-ref/source/monplace.h +++ b/crawl-ref/source/monplace.h @@ -340,6 +340,18 @@ coord_def find_newmons_square_contiguous(monster_type mons_class, void spawn_random_monsters(); +/* *********************************************************************** + * 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(); + class monster_pathfind { public: diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc index 9f1bae2103..ce8154f240 100644 --- a/crawl-ref/source/tags.cc +++ b/crawl-ref/source/tags.cc @@ -1726,7 +1726,10 @@ static void tag_construct_level(writer &th) marshallCoord(th, env.sanctuary_pos); marshallByte(th, env.sanctuary_time); + marshallLong(th, env.spawn_random_rate); + env.markers.write(th); + env.properties.write(th); } void marshallItem(writer &th, const item_def &item) @@ -2003,6 +2006,7 @@ void tag_construct_level_tiles(writer &th) static void tag_read_level( reader &th, char minorVersion ) { + env.floor_colour = unmarshallByte(th); env.rock_colour = unmarshallByte(th); @@ -2070,7 +2074,12 @@ static void tag_read_level( reader &th, char minorVersion ) unmarshallCoord(th, env.sanctuary_pos); env.sanctuary_time = unmarshallByte(th); + env.spawn_random_rate = unmarshallLong(th); + env.markers.read(th, minorVersion); + + env.properties.clear(); + env.properties.read(th); } static void tag_read_level_items(reader &th, char minorVersion) diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc index ea38dd925a..ecef971888 100644 --- a/crawl-ref/source/travel.cc +++ b/crawl-ref/source/travel.cc @@ -3118,6 +3118,17 @@ level_id level_id::parse_level_id(const std::string &s) throw (std::string) return level_id(br, dep); } +level_id level_id::from_packed_place(const unsigned short place) +{ + level_id id; + + id.branch = (branch_type) place_branch(place); + id.depth = place_depth(place); + id.level_type = (level_area_type) place_type(place); + + return (id); +} + // NOTE: see also marshall_level_id void level_id::save(writer& outf) const { |