/* * mapdef.h: * Header for map structures used by the level compiler. * Created by: dshaligram on Wed Nov 22 08:41:20 2006 UTC * * NOTE: When we refer to map, this could be a full map, filling an entire * level or a minivault that occupies just a portion of the level. */ #ifndef __MAPDEF_H__ #define __MAPDEF_H__ #include #include #include #include #include "dlua.h" #include "enum.h" #include "externs.h" #include "matrix.h" #include "fprop.h" #include "makeitem.h" #include "travel_defs.h" // [dshaligram] Maps can be mirrored; for every orientation, there must be // a suitable mirror. enum map_section_type // see maps.cc and dungeon.cc {dlb} { MAP_NONE = -1, MAP_NORTH = 1, // 1 MAP_SOUTH, MAP_EAST, MAP_WEST, MAP_NORTHWEST, // 5 MAP_NORTHEAST, MAP_SOUTHWEST, MAP_SOUTHEAST, MAP_ENCOMPASS, // A "floating" vault is placed somewhat like a minivault, away from // the edges, although in other respects it behaves like a regular vault. MAP_FLOAT, MAP_NUM_SECTION_TYPES }; struct raw_range { branch_type branch; int shallowest, deepest; bool deny; }; struct level_range { public: level_area_type level_type; branch_type branch; int shallowest, deepest; bool deny; public: level_range(const raw_range &range); level_range(branch_type br = BRANCH_MAIN_DUNGEON, int s = -1, int d = -1); void set(int s, int d = -1); void set(const std::string &branch, int s, int d) throw (std::string); void reset(); bool matches(const level_id &) const; bool matches(int depth) const; void write(writer&) const; void read(reader&); bool valid() const; int span() const; static level_range parse(std::string lr) throw (std::string); std::string describe() const; std::string str_depth_range() const; bool operator == (const level_range &lr) const; operator raw_range () const; operator std::string () const { return describe(); } private: static void parse_partial(level_range &lr, const std::string &s) throw (std::string); static void parse_depth_range(const std::string &s, int *low, int *high) throw (std::string); }; typedef std::pair glyph_weighted_replacement_t; typedef std::vector glyph_replacements_t; class map_lines; class subst_spec { public: subst_spec(std::string torepl, bool fix, const glyph_replacements_t &repls); subst_spec(int count, bool fix, const glyph_replacements_t &repls); subst_spec() : key(""), count(-1), fix(false), frozen_value(0), repl() { } int value(); public: std::string key; // If this is part of an nsubst spec, how many to replace. // -1 corresponds to all (i.e. '*') int count; bool fix; int frozen_value; glyph_replacements_t repl; }; class nsubst_spec { public: nsubst_spec(std::string key, const std::vector &specs); public: std::string key; std::vector specs; }; typedef std::pair map_weighted_colour; class map_colour_list : public std::vector { public: bool parse(const std::string &s, int weight); }; class colour_spec { public: colour_spec(std::string _key, bool _fix, const map_colour_list &clist) : key(_key), fix(_fix), fixed_colour(BLACK), colours(clist) { } int get_colour(); public: std::string key; bool fix; int fixed_colour; map_colour_list colours; }; typedef std::pair map_weighted_fprop; class map_fprop_list : public std::vector { public: bool parse(const std::string &fp, int weight); }; class fprop_spec { public: fprop_spec(std::string _key, bool _fix, const map_fprop_list &flist) : key(_key), fix(_fix), fixed_prop(FPROP_NONE), fprops(flist) { } unsigned long get_property(); public: std::string key; bool fix; unsigned long fixed_prop; map_fprop_list fprops; }; #ifdef USE_TILE typedef std::pair map_weighted_tile; class map_tile_list : public std::vector { public: bool parse(const std::string &s, int weight); }; class tile_spec { public: tile_spec(const std::string &_key, bool _fix, bool _rand, bool _floor, bool _feat, const map_tile_list &_tiles) : key(_key), fix(_fix), chose_fixed(false), no_random(_rand), floor(_floor), feat(_feat), fixed_tile(0), tiles(_tiles) { } int get_tile(); public: std::string key; bool fix; bool chose_fixed; bool no_random; bool floor; bool feat; int fixed_tile; map_tile_list tiles; }; #endif class map_marker_spec { public: std::string key; std::string marker; // Special handling for Lua markers: std::auto_ptr lua_fn; map_marker_spec(std::string _key, const std::string &mark) : key(_key), marker(mark), lua_fn() { } map_marker_spec(std::string _key, const lua_datum &fn) : key(_key), marker(), lua_fn(new lua_datum(fn)) { } std::string apply_transform(map_lines &map); private: map_marker *create_marker(); }; typedef std::pair map_weighted_string; class map_string_list : public std::vector { public: bool parse(const std::string &fp, int weight); }; class string_spec { public: string_spec(std::string _key, bool _fix, const map_string_list &slist) : key(_key), fix(_fix), fixed_str(""), strlist(slist) { } std::string get_property(); public: std::string key; bool fix; std::string fixed_str; map_string_list strlist; }; template std::string parse_weighted_str(const std::string &cspec, T &list); class map_def; class rectangle_iterator; struct keyed_mapspec; class map_lines { public: map_lines(); map_lines(const map_lines &); ~map_lines(); map_lines &operator = (const map_lines &); bool in_map(const coord_def &pos) const; void add_line(const std::string &s); std::string add_nsubst(const std::string &st); std::string add_subst(const std::string &st); std::string add_shuffle(const std::string &s); std::string add_colour(const std::string &col); std::string add_fproperty(const std::string &sub); void clear_markers(); #ifdef USE_TILE std::string add_floortile(const std::string &s); std::string add_rocktile(const std::string &s); std::string add_spec_tile(const std::string &s); #endif std::vector find_glyph(const std::string &glyphs) const; std::vector find_glyph(int glyph) const; coord_def find_first_glyph(int glyph) const; coord_def find_first_glyph(const std::string &glyphs) const; // Find rectangular bounds (inclusive) for uses of the glyph in the map. // Returns false if glyph could not be found. bool find_bounds(int glyph, coord_def &tl, coord_def &br) const; // Same as above, but for any of the glyphs in glyph_str. bool find_bounds(const char *glyph_str, coord_def &tl, coord_def &br) const; void set_orientation(const std::string &s); int width() const; int height() const; coord_def size() const; int glyph(int x, int y) const; int glyph(const coord_def &) const; bool is_solid(int gly) const; bool solid_borders(map_section_type border); // Make all lines the same length. void normalise(char fillc = ' '); // Rotate 90 degrees either clockwise or anticlockwise void rotate(bool clockwise); void hmirror(); void vmirror(); void clear(); void add_marker(map_marker *marker); std::string add_feature_marker(const std::string &desc); std::string add_lua_marker(const std::string &key, const lua_datum &fn); void apply_markers(const coord_def &pos); void apply_grid_overlay(const coord_def &pos); void apply_overlays(const coord_def &pos); const std::vector &get_lines() const; std::vector &get_lines(); rectangle_iterator get_iter() const; char operator () (const coord_def &c) const; char& operator () (const coord_def &c); char operator () (int x, int y) const; char& operator () (int x, int y); const keyed_mapspec *mapspec_at(const coord_def &c) const; keyed_mapspec *mapspec_at(const coord_def &c); std::string add_key_item(const std::string &s); std::string add_key_mons(const std::string &s); std::string add_key_feat(const std::string &s); std::string add_key_mask(const std::string &s); bool in_bounds(const coord_def &c) const; // Extend map dimensions with glyph 'fill' to minimum width and height. void extend(int min_width, int min_height, char fill); bool fill_zone(travel_distance_grid_t &tpd, const coord_def &start, const coord_def &tl, const coord_def &br, int zone, const char *wanted, const char *passable) const; int count_feature_in_box(const coord_def &tl, const coord_def &br, const char *feat) const; void fill_mask_matrix(const std::string &glyphs, const coord_def &tl, const coord_def &br, Matrix &flags); // Merge vault onto the tl/br subregion, where mask is true. void merge_subvault(const coord_def &tl, const coord_def &br, const Matrix &mask, const map_def &vault); private: void init_from(const map_lines &map); template void clear_vector(V &vect); void vmirror_markers(); void hmirror_markers(); void rotate_markers(bool clock); void vmirror_marker(map_marker *, int par); void hmirror_marker(map_marker *, int par); void rotate_marker(map_marker *, int par); void translate_marker(void (map_lines::*xform)(map_marker *, int par), int par = 0); void resolve_shuffle(const std::string &shuffle); void subst(std::string &s, subst_spec &spec); void subst(subst_spec &); void nsubst(nsubst_spec &); void overlay_colours(colour_spec &); void overlay_fprops(fprop_spec &); // Merge cell (vx, vy) from vault onto this map's (x, y) cell. typedef FixedVector keyspec_map; void merge_cell(int x, int y, const map_def &vault, int vx, int vy, int keyspec_idx); #ifdef USE_TILE void overlay_tiles(tile_spec &); #endif void check_borders(); std::string shuffle(std::string s); std::string block_shuffle(const std::string &s); std::string check_shuffle(std::string &s); std::string check_block_shuffle(const std::string &s); std::string clean_shuffle(std::string s); std::string parse_nsubst_spec(const std::string &s, subst_spec &spec); int apply_nsubst(std::vector &pos, int start, int nsub, subst_spec &spec); std::string parse_glyph_replacements(std::string s, glyph_replacements_t &gly); #ifdef USE_TILE std::string add_tile(const std::string &sub, bool is_floor, bool is_feat); #endif std::string add_key_field( const std::string &s, std::string (keyed_mapspec::*set_field)( const std::string &s, bool fixed), void (keyed_mapspec::*copy_field)(const keyed_mapspec &spec)); const keyed_mapspec *mapspec_for_key(int key) const; keyed_mapspec *mapspec_for_key(int key); friend class subst_spec; friend class nsubst_spec; friend class shuffle_spec; friend class map_marker_spec; friend class colour_spec; friend class tile_spec; private: std::vector markers; std::vector lines; struct overlay_def { overlay_def() : colour(0), rocktile(0), floortile(0), tile(0), no_random(false), property(0), keyspec_idx(0) {} int colour; int rocktile; int floortile; int tile; bool no_random; int property; int keyspec_idx; }; typedef Matrix overlay_matrix; std::auto_ptr overlay; typedef std::map keyed_specs; keyed_specs keyspecs; int next_keyspec_idx; enum { SUBVAULT_GLYPH = 1 }; int map_width; bool solid_north, solid_east, solid_south, solid_west; bool solid_checked; }; enum item_spec_type { ISPEC_GOOD = -2, ISPEC_SUPERB = -3, ISPEC_DAMAGED = -4, ISPEC_BAD = -5, ISPEC_RANDART = -6, ISPEC_ACQUIREMENT = -9 }; struct item_spec { int genweight; object_class_type base_type; int sub_type; int plus, plus2; int ego; int allow_uniques; int level; int race; int qty; int acquirement_source; level_id place; // Specifically for storing information about randart spell books. CrawlHashTable props; item_spec() : genweight(10), base_type(OBJ_RANDOM), sub_type(OBJ_RANDOM), plus(-1), plus2(-1), ego(0), allow_uniques(1), level(-1), race(MAKE_ITEM_RANDOM_RACE), qty(0), acquirement_source(0), place(), props() { } }; typedef std::vector 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(); } bool empty() const { return items.empty(); } std::string add_item(const std::string &spec, bool fix = false); std::string set_item(int index, const std::string &spec); // Set this list to be a copy of the item_spec_slot in list. void set_from_slot(const item_list &list, int slot_index); 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); int parse_acquirement_source(const std::string &source); 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 items; std::string error; }; class mons_spec { public: int mid; level_id place; monster_type monbase; // Base monster for zombies and dracs. mon_attitude_type attitude; int number; // Head count for hydras, etc. int quantity; // Number of monsters (usually 1). int genweight, mlevel; bool fix_mons; bool generate_awake; bool patrolling; bool band; int colour; int hd; int hp; int abjuration_duration; int summon_type; item_list items; std::string monname; std::string non_actor_summoner; bool explicit_spells; monster_spells spells; unsigned long extra_monster_flags; CrawlHashTable props; mons_spec(int id = RANDOM_MONSTER, monster_type base = MONS_NO_MONSTER, int num = 0, int gw = 10, int ml = 0, bool _fixmons = false, bool awaken = false, bool patrol = false) : mid(id), place(), monbase(base), attitude(ATT_HOSTILE), number(num), quantity(1), genweight(gw), mlevel(ml), fix_mons(_fixmons), generate_awake(awaken), patrolling(false), band(false), colour(BLACK), hd(0), hp(0), abjuration_duration(0), summon_type(0), items(), monname(""), non_actor_summoner(""), explicit_spells(false), spells(), extra_monster_flags(0L), props() { } }; class mons_list { public: mons_list(); void clear(); // Set this list to be a copy of the mons_spec_slot in list. void set_from_slot(const mons_list &list, int slot_index); 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 slot_size(int slot) const { return mons[slot].mlist.size(); } private: typedef std::vector mons_spec_list; struct mons_spec_slot { mons_spec_list mlist; bool fix_slot; mons_spec_slot(const mons_spec_list &list, bool fix = false) : mlist(list), fix_slot(fix) { } mons_spec_slot() : mlist(), fix_slot(false) { } }; private: mons_spec mons_by_name(std::string name) const; mons_spec drac_monspec(std::string name) const; void get_zombie_type(std::string s, mons_spec &spec) const; mons_spec get_hydra_spec(const std::string &name) const; mons_spec get_slime_spec(const std::string &name) const; mons_spec get_zombified_monster(const std::string &name, monster_type zomb) const; mons_spec_slot parse_mons_spec(std::string spec); void parse_mons_spells(mons_spec &slot, const std::string &spells); mons_spec pick_monster(mons_spec_slot &slot); int fix_demon(int id) const; bool check_mimic(const std::string &s, int *mid, bool *fix) const; private: std::vector< mons_spec_slot > mons; std::string error; }; 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_list; struct feature_slot { feature_spec_list feats; bool fix_slot; feature_slot(); feature_spec get_feat(int default_glyph); }; struct map_flags { unsigned long flags_set, flags_unset; map_flags(); void clear(); static map_flags parse(const std::string flag_list[], const std::string &s) throw(std::string); }; struct keyed_mapspec { public: int key_glyph; feature_slot feat; item_list item; mons_list mons; map_flags map_mask; public: keyed_mapspec(); // Parse the string and set the given entry. If fix is true, // then whatever is selected for the first feature will be // permanently fixed. 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); std::string set_mask(const std::string &s, bool garbage); // Copy from the given mapspec. If that entry is fixed, // it should be pre-selected prior to the copy. void copy_feat(const keyed_mapspec &spec); void copy_mons(const keyed_mapspec &spec); void copy_item(const keyed_mapspec &spec); void copy_mask(const keyed_mapspec &spec); feature_spec get_feat(); mons_list &get_monsters(); item_list &get_items(); map_flags &get_mask(); 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); }; typedef std::vector depth_ranges; class map_def; struct dlua_set_map { dlua_set_map(map_def *map); ~dlua_set_map(); }; class map_def; dungeon_feature_type map_feature_at(map_def *map, const coord_def &c, int rawfeat); struct map_file_place { std::string filename; int lineno; map_file_place(const std::string &s = "", int line = 0) : filename(s), lineno(line) { } void clear() { filename.clear(); lineno = 0; } }; ///////////////////////////////////////////////////////////////////////////// // map_def: map definitions for maps loaded from .des files. // // Please read this before changing map_def. // // When adding Lua-visible fields to map_def, note that there are two // kinds of fields: // // * Fields that determine placement of the map, or are unchanging, // such as "place", "depths" (determine placement), or "name" (does // not change between different evaluations of the map). Such fields // must be reset to their default values in map_def::init() if they // determine placement, or just initialised in the constructor if // they will not change. // // * Fields that do not determine placement and may change between // different uses of the map (such as "mons", "items", // "level_flags", etc.). Such fields must be reset to their default // values in map_def::reinit(), which is called before the map is // used. // // If you do not do this, maps will not work correctly, and will break // in obscure, hard-to-find ways. The level-compiler will not (cannot) // warn you. // class map_def { public: std::string name; std::string tags; level_id place; depth_ranges depths; map_section_type orient; int chance_priority; int chance; int weight; int weight_depth_mult; int weight_depth_div; std::vector welcome_messages; map_lines map; mons_list mons; item_list items; static bool valid_item_array_glyph(int gly); static int item_array_glyph_to_slot(int gly); static bool valid_monster_array_glyph(int gly); static bool valid_monster_glyph(int gly); static int monster_array_glyph_to_slot(int gly); std::vector random_mons; map_flags level_flags, branch_flags; dlua_chunk prelude, mapchunk, main, validate, veto; map_file_place place_loaded_from; map_def *original; unsigned char rock_colour, floor_colour; int rock_tile, floor_tile; dungeon_feature_type border_fill_type; private: // This map has been loaded from an index, and not fully realised. bool index_only; long cache_offset; std::string file; typedef Matrix subvault_mask; subvault_mask *svmask; // True if this map is in the process of being validated. bool validating_map_flag; public: map_def(); void init(); void reinit(); void load(); bool in_map(const coord_def &p) const; coord_def size() const { return coord_def(map.width(), map.height()); } std::vector find_glyph(int glyph) const; coord_def find_first_glyph(int glyph) const; coord_def find_first_glyph(const std::string &glyphs) const; void write_index(writer&) const; void write_full(writer&); void read_index(reader&); void read_full(reader&); void set_file(const std::string &s); std::string run_lua(bool skip_main); // Returns true if the validation passed. bool test_lua_validate(bool croak = false); // Returns true if *not* vetoed, i.e., the map is good to go. bool test_lua_veto(); std::string validate_map_def(); std::string validate_temple_map(); // Returns true if this map is in the middle of validation. bool is_validating() const { return (validating_map_flag); } void add_prelude_line(int line, const std::string &s); void add_main_line(int line, const std::string &s); void hmirror(); void vmirror(); void rotate(bool clockwise); void normalise(); std::string resolve(); void fixup(); bool is_usable_in(const level_id &lid) const; const keyed_mapspec *mapspec_at(const coord_def &c) const; keyed_mapspec *mapspec_at(const coord_def &c); bool has_depth() const; void add_depth(const level_range &depth); void add_depths(depth_ranges::const_iterator s, depth_ranges::const_iterator e); bool can_dock(map_section_type) const; coord_def dock_pos(map_section_type) const; coord_def float_dock(); coord_def float_place(); coord_def float_aligned_place() const; coord_def float_random_place() const; std::vector anchor_points() const; bool is_minivault() const; bool has_tag(const std::string &tag) const; bool has_tag_prefix(const std::string &tag) const; bool has_tag_suffix(const std::string &suffix) const; std::vector get_tags() const; std::vector get_shuffle_strings() const; std::vector get_subst_strings() const; int glyph_at(const coord_def &c) const; // Subvault functions. std::string subvault_from_tagstring(const std::string &s); bool is_subvault() const; void apply_subvault_mask(); bool subvault_cell_valid(const coord_def &c) const; int subvault_width() const; int subvault_height() const; // Returns the number of cells in the subvault map that // will not get copied to parent vault at a given placement. int subvault_mismatch_count(const coord_def &place) const; public: struct map_feature_finder { map_def ↦ map_feature_finder(map_def &map_) : map(map_) { } // This may actually modify the underlying map by fixing KFEAT: // feature slots, but that's fine by us. dungeon_feature_type operator () (const coord_def &c) const { return (map_feature_at(&map, c, -1)); } }; struct map_bounds_check { map_def ↦ map_bounds_check(map_def &map_) : map(map_) { } bool operator () (const coord_def &c) const { return (c.x >= 0 && c.x < map.map.width() && c.y >= 0 && c.y < map.map.height()); } }; private: void write_depth_ranges(writer&) const; void read_depth_ranges(reader&); bool test_lua_boolchunk(dlua_chunk &, bool def = false, bool croak = false); std::string rewrite_chunk_errors(const std::string &s) const; std::string apply_subvault(string_spec &); }; const int CHANCE_ROLL = 10000; std::string escape_string(std::string in, const std::string &toesc, const std::string &escapewith); std::string mapdef_split_key_item(const std::string &s, std::string *key, int *separator, std::string *arg, int key_max_len = 1); const char *map_section_name(int msect); #endif