diff options
73 files changed, 1524 insertions, 881 deletions
diff --git a/crawl-ref/source/actor.h b/crawl-ref/source/actor.h index f8e920eb14..b29e826307 100644 --- a/crawl-ref/source/actor.h +++ b/crawl-ref/source/actor.h @@ -28,7 +28,15 @@ public: virtual bool is_summoned(int* duration = NULL, int* summon_type = NULL) const = 0; + // [ds] Low-level moveto() - moves the actor without updating relevant + // grids, such as mgrd. virtual void moveto(const coord_def &c) = 0; + + // High-level actor movement. If in doubt, use this. Returns true if the + // actor cannot be moved to the target, possibly because it is already + // occupied. + virtual bool move_to_pos(const coord_def &c) = 0; + virtual void set_position(const coord_def &c); virtual const coord_def& pos() const { return position; } diff --git a/crawl-ref/source/artefact.cc b/crawl-ref/source/artefact.cc index 9b2cc6feed..71ae7b27cd 100644 --- a/crawl-ref/source/artefact.cc +++ b/crawl-ref/source/artefact.cc @@ -1923,7 +1923,7 @@ bool make_item_randart( item_def &item, bool force_mundane ) if (item.flags & ISFLAG_UNRANDART) return (false); - if (item_is_mundane(item) && !one_chance_in(5) && !force_mundane) + if (item.is_mundane() && !one_chance_in(5) && !force_mundane) return (false); ASSERT(!item.props.exists(KNOWN_PROPS_KEY)); diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index cdeefc8204..8b05a833d9 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -3243,17 +3243,18 @@ void bolt::affect_place_clouds() if (p == you.pos()) { mprf("The %s you are in turns into %s!", - cloud_name(ctype).c_str(), cloud_name(new_type).c_str()); + cloud_name(cloudidx).c_str(), cloud_name(new_type).c_str()); obvious_effect = true; } else if (you.see_cell(p)) { mprf("A cloud of %s turns into %s.", - cloud_name(ctype).c_str(), cloud_name(new_type).c_str()); + cloud_name(cloudidx).c_str(), cloud_name(new_type).c_str()); obvious_effect = true; } ctype = new_type; + env.cloud[cloudidx].name = ""; return; } diff --git a/crawl-ref/source/cloud.cc b/crawl-ref/source/cloud.cc index 638432f504..58eb185c97 100644 --- a/crawl-ref/source/cloud.cc +++ b/crawl-ref/source/cloud.cc @@ -27,6 +27,11 @@ #include "stuff.h" #include "env.h" #include "terrain.h" +#ifdef USE_TILE +#include "tiles.h" +#include "tiledef-gui.h" +#include "tiledef-main.h" +#endif #include "mutation.h" static int _actual_spread_rate(cloud_type type, int spread_rate) @@ -70,7 +75,8 @@ static bool _killer_whose_match(kill_category whose, killer_type killer) static void _new_cloud( int cloud, cloud_type type, const coord_def& p, int decay, kill_category whose, killer_type killer, - unsigned char spread_rate ) + unsigned char spread_rate, int colour, std::string name, + std::string tile) { ASSERT( env.cloud[cloud].type == CLOUD_NONE ); ASSERT(_killer_whose_match(whose, killer)); @@ -83,13 +89,28 @@ static void _new_cloud( int cloud, cloud_type type, const coord_def& p, c.whose = whose; c.killer = killer; c.spread_rate = spread_rate; + c.colour = colour; + c.name = name; +#ifdef USE_TILE + if (!tile.empty()) + { + unsigned int index; + if (!tile_main_index(tile.c_str(), index)) + { + mprf(MSGCH_ERROR, "Invalid tile requested for cloud: '%s'.", tile.c_str()); + tile = ""; + } + } +#endif + c.tile = tile; env.cgrid(p) = cloud; env.cloud_no++; } static void _place_new_cloud(cloud_type cltype, const coord_def& p, int decay, kill_category whose, killer_type killer, - int spread_rate) + int spread_rate, int colour, std::string name, + std::string tile) { if (env.cloud_no >= MAX_CLOUDS) return; @@ -99,7 +120,8 @@ static void _place_new_cloud(cloud_type cltype, const coord_def& p, int decay, { if (env.cloud[ci].type == CLOUD_NONE) // i.e., is empty { - _new_cloud( ci, cltype, p, decay, whose, killer, spread_rate ); + _new_cloud( ci, cltype, p, decay, whose, killer, spread_rate, colour, + name, tile ); break; } } @@ -129,7 +151,7 @@ static int _spread_cloud(const cloud_struct &cloud) newdecay = cloud.decay - 1; _place_new_cloud( cloud.type, *ai, newdecay, cloud.whose, cloud.killer, - cloud.spread_rate ); + cloud.spread_rate, cloud.colour, cloud.name, cloud.tile ); extra_decay += 8; } @@ -151,7 +173,8 @@ static void _spread_fire(const cloud_struct &cloud) // burning trees produce flames all around if (!cell_is_solid(*ai) && make_flames) _place_new_cloud( CLOUD_FIRE, *ai, cloud.decay/2+1, cloud.whose, - cloud.killer, cloud.spread_rate ); + cloud.killer, cloud.spread_rate, cloud.colour, + cloud.name, cloud.tile ); // forest fire doesn't spread in all directions at once, // every neighbouring square gets a separate roll @@ -161,7 +184,8 @@ static void _spread_fire(const cloud_struct &cloud) mpr("The forest fire spreads!"); grd(*ai) = DNGN_FLOOR; _place_new_cloud( cloud.type, *ai, random2(30)+25, cloud.whose, - cloud.killer, cloud.spread_rate ); + cloud.killer, cloud.spread_rate, cloud.colour, + cloud.name, cloud.tile ); } } @@ -248,6 +272,9 @@ void delete_cloud( int cloud ) c.whose = KC_OTHER; c.killer = KILL_NONE; c.spread_rate = 0; + c.colour = -1; + c.name = ""; + c.tile = ""; env.cgrid(c.pos) = EMPTY_CLOUD; c.pos.reset(); @@ -271,32 +298,37 @@ void move_cloud( int cloud, const coord_def& newpos ) // Places a cloud with the given stats assuming one doesn't already // exist at that point. void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, - kill_category whose, int spread_rate ) + kill_category whose, int spread_rate, int colour, + std::string name, std::string tile) { check_place_cloud(cl_type, p, lifetime, whose, - cloud_struct::whose_to_killer(whose), spread_rate); + cloud_struct::whose_to_killer(whose), spread_rate, colour, + name, tile); } // Places a cloud with the given stats assuming one doesn't already // exist at that point. void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, - killer_type killer, int spread_rate ) + killer_type killer, int spread_rate, int colour, + std::string name, std::string tile) { check_place_cloud(cl_type, p, lifetime, cloud_struct::killer_to_whose(killer), killer, - spread_rate); + spread_rate, colour, name, tile); } // Places a cloud with the given stats assuming one doesn't already // exist at that point. void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, kill_category whose, killer_type killer, - int spread_rate ) + int spread_rate, int colour, std::string name, + std::string tile) { if (!in_bounds(p) || env.cgrid(p) != EMPTY_CLOUD) return; - place_cloud( cl_type, p, lifetime, whose, killer, spread_rate ); + place_cloud( cl_type, p, lifetime, whose, killer, spread_rate, colour, + name, tile ); } int steam_cloud_damage(const cloud_struct &cloud) @@ -317,27 +349,32 @@ int steam_cloud_damage(int decay) // make way if there are too many on level. Will overwrite an old // cloud under some circumstances. void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range, - kill_category whose, int _spread_rate) + kill_category whose, int _spread_rate, int colour, + std::string name, std::string tile) { place_cloud(cl_type, ctarget, cl_range, whose, - cloud_struct::whose_to_killer(whose), _spread_rate); + cloud_struct::whose_to_killer(whose), _spread_rate, + colour, name, tile); } // Places a cloud with the given stats. May delete old clouds to // make way if there are too many on level. Will overwrite an old // cloud under some circumstances. void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range, - killer_type killer, int _spread_rate) + killer_type killer, int _spread_rate, int colour, + std::string name, std::string tile) { place_cloud(cl_type, ctarget, cl_range, - cloud_struct::killer_to_whose(killer), killer, _spread_rate); + cloud_struct::killer_to_whose(killer), killer, _spread_rate, + colour, name, tile); } // Places a cloud with the given stats. May delete old clouds to // make way if there are too many on level. Will overwrite an old // cloud under some circumstances. void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range, - kill_category whose, killer_type killer, int _spread_rate) + kill_category whose, killer_type killer, int _spread_rate, + int colour, std::string name, std::string tile) { if (is_sanctuary(ctarget) && !is_harmless_cloud(cl_type)) return; @@ -393,7 +430,7 @@ void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range, if (cl_new != -1) { _new_cloud( cl_new, cl_type, ctarget, cl_range * 10, - whose, killer, spread_rate ); + whose, killer, spread_rate, colour, name, tile ); } else { @@ -403,7 +440,7 @@ void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range, if (env.cloud[ci].type == CLOUD_NONE) // ie is empty { _new_cloud( ci, cl_type, ctarget, cl_range * 10, - whose, killer, spread_rate ); + whose, killer, spread_rate, colour, name, tile ); break; } } @@ -609,6 +646,7 @@ void in_a_cloud() int cl = env.cgrid(you.pos()); int hurted = 0; int resist; + std::string name = env.cloud[cl].name; if (you.duration[DUR_CONDENSATION_SHIELD] > 0) remove_condensation_shield(); @@ -620,7 +658,7 @@ void in_a_cloud() if (you.duration[DUR_FIRE_SHIELD]) return; - mpr("You are engulfed in roaring flames!"); + mprf("You are engulfed in %s!", !name.empty() ? name.c_str() : "roaring flames"); resist = player_res_fire(); @@ -650,7 +688,8 @@ void in_a_cloud() case CLOUD_STINK: // If you don't have to breathe, unaffected - mpr("You are engulfed in noxious fumes!"); + mprf("You are engulfed in %s!", !name.empty() ? name.c_str() : "noxious fumes"); + if (player_res_poison()) break; @@ -674,7 +713,7 @@ void in_a_cloud() if (you.mutation[MUT_PASSIVE_FREEZE]) break; - mpr("You are engulfed in freezing vapours!"); + mprf("You are engulfed in %s!", !name.empty() ? name.c_str() : "freezing vapours"); resist = player_res_cold(); @@ -703,7 +742,8 @@ void in_a_cloud() case CLOUD_POISON: // If you don't have to breathe, unaffected - mpr("You are engulfed in poison gas!"); + mprf("You are engulfed in %s!", !name.empty() ? name.c_str() : "poison gas"); + if (!player_res_poison()) { ouch((random2(10) * you.time_taken) / 10, cl, KILLED_BY_CLOUD, @@ -717,12 +757,14 @@ void in_a_cloud() case CLOUD_TLOC_ENERGY: case CLOUD_PURPLE_SMOKE: case CLOUD_BLACK_SMOKE: - mpr("You are engulfed in a cloud of smoke!"); + mprf("You are engulfed in %s!", !name.empty() ? name.c_str() : "a cloud of smoke"); + break; case CLOUD_STEAM: { - mpr("You are engulfed in a cloud of scalding steam!"); + mprf("You are engulfed in %s!", !name.empty() ? name.c_str() : "a cloud of scalding steam"); + if (player_res_steam() > 0) { mpr("It doesn't seem to affect you."); @@ -746,7 +788,7 @@ void in_a_cloud() } case CLOUD_MIASMA: - mpr("You are engulfed in a dark miasma."); + mprf("You are engulfed in %s!", !name.empty() ? name.c_str() : "a dark miasma"); if (you.res_rotting()) return; @@ -771,11 +813,15 @@ void in_a_cloud() you.duration[DUR_MISLED] = 0; } - mpr("You are standing in the rain."); + if (name.empty() || name == "the rain") + mpr("You are standing in the rain."); + else + mprf("You are engulfed in %s.", name.c_str()); + break; case CLOUD_MUTAGENIC: - mpr("You are engulfed in a mutagenic fog!"); + mprf("You are engulfed in %s!", !name.empty() ? name.c_str() : "a mutagenic fog"); if (coinflip()) { @@ -870,6 +916,14 @@ cloud_type in_what_cloud() return (env.cloud[cl].type); } +std::string cloud_name(int cloudno) +{ + if (!env.cloud[cloudno].name.empty()) + return (env.cloud[cloudno].name); + else + return cloud_name(env.cloud[cloudno].type); +} + std::string cloud_name(cloud_type type) { switch (type) @@ -975,6 +1029,9 @@ void cloud_struct::set_killer(killer_type _killer) int get_cloud_colour(int cloudno) { int which_colour = LIGHTGREY; + if (env.cloud[cloudno].colour != -1) + return (env.cloud[cloudno].colour); + switch (env.cloud[cloudno].type) { case CLOUD_FIRE: diff --git a/crawl-ref/source/cloud.h b/crawl-ref/source/cloud.h index 9cd1ef249c..9bb5ba7f00 100644 --- a/crawl-ref/source/cloud.h +++ b/crawl-ref/source/cloud.h @@ -34,19 +34,30 @@ void delete_cloud( int cloud ); void move_cloud( int cloud, const coord_def& newpos ); void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, - kill_category whose, int spread_rate = -1 ); + kill_category whose, int spread_rate = -1, + int colour = -1, std::string name = "", + std::string tile = ""); void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, - killer_type killer, int spread_rate = -1 ); + killer_type killer, int spread_rate = -1, + int colour = -1, std::string name = "", + std::string tile = ""); void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, kill_category whose, killer_type killer, - int spread_rate = -1 ); + int spread_rate = -1, + int colour = -1, std::string name = "", + std::string tile = ""); void place_cloud( cloud_type cl_type, const coord_def& ctarget, - int cl_range, kill_category whose, int spread_rate = -1 ); + int cl_range, kill_category whose, int spread_rate = -1, + int colour = -1, std::string name = "", + std::string tile = ""); void place_cloud( cloud_type cl_type, const coord_def& ctarget, - int cl_range, killer_type killer, int spread_rate = -1 ); + int cl_range, killer_type killer, int spread_rate = -1, + int colour = -1, std::string name = "", + std::string tile = ""); void place_cloud( cloud_type cl_type, const coord_def& ctarget, int cl_range, kill_category whose, killer_type killer, - int spread_rate = -1 ); + int spread_rate = -1, int colour = -1, std::string name = "", + std::string tile = ""); void manage_clouds(void); @@ -61,6 +72,7 @@ int resist_fraction(int resist, int bonus_res = 0); int max_cloud_damage(cloud_type cl_type, int power = -1); void in_a_cloud(void); +std::string cloud_name(int cloudno); std::string cloud_name(cloud_type type); int get_cloud_colour(int cloudno); diff --git a/crawl-ref/source/dat/clua/lm_fog.lua b/crawl-ref/source/dat/clua/lm_fog.lua index 17b80e652e..ace95fec53 100644 --- a/crawl-ref/source/dat/clua/lm_fog.lua +++ b/crawl-ref/source/dat/clua/lm_fog.lua @@ -52,6 +52,10 @@ -- start_clouds: The number of clouds to lay when the level containing -- the cloud machine is entered. This is necessary since clouds -- are cleared when the player leaves a level. +-- colour: A string value with which to recolour the cloud. +-- name: A string value with which to rebrand (specifically, rename) the +-- cloud in question. +-- tile: A string value with which to retile the cloud. -- listener: A table with a function field called 'func'. Will be called -- whenever the countdown is activated, and whenever the fog -- machine is reset. It will be called with a reference to the table, @@ -100,6 +104,9 @@ function FogMachine:new(pars) m.size_max = pars.size_max or pars.size m.spread_rate = pars.spread_rate or -1 m.start_clouds = pars.start_clouds or 1 + m.colour = pars.colour or "" + m.name = pars.name or "" + m.tile = pars.tile or "" m.size_buildup_amnt = pars.size_buildup_amnt or 0 m.size_buildup_time = pars.size_buildup_time or 1 @@ -125,9 +132,10 @@ function FogMachine:new(pars) end function FogMachine:apply_cloud(point, pow_min, pow_max, pow_rolls, - size, cloud_type, kill_cat, spread) + size, cloud_type, kill_cat, spread, colour, + name, tile) dgn.apply_area_cloud(point.x, point.y, pow_min, pow_max, pow_rolls, size, - cloud_type, kill_cat, spread) + cloud_type, kill_cat, spread, colour, name, tile) end function FogMachine:do_fog(point) @@ -165,7 +173,8 @@ function FogMachine:do_fog(point) self:apply_cloud(p, self.pow_min, self.pow_max, self.pow_rolls, crawl.random_range(size_min, size_max, 1), - self.cloud_type, self.kill_cat, spread) + self.cloud_type, self.kill_cat, spread, self.colour, + self.name, self.tile) end function FogMachine:do_trigger(triggerer, marker, ev) @@ -225,6 +234,9 @@ function FogMachine:write(marker, th) file.marshall(th, self.spread_buildup_amnt) file.marshall(th, self.spread_buildup_time) file.marshall(th, self.buildup_turns) + file.marshall(th, self.colour) + file.marshall(th, self.name) + file.marshall(th, self.tile) end function FogMachine:read(marker, th) @@ -245,6 +257,9 @@ function FogMachine:read(marker, th) self.spread_buildup_amnt = file.unmarshall_number(th) self.spread_buildup_time = file.unmarshall_number(th) self.buildup_turns = file.unmarshall_number(th) + self.colour = file.unmarshall_string(th) + self.name = file.unmarshall_string(th) + self.tile = file.unmarshall_string(th) setmetatable(self, FogMachine) diff --git a/crawl-ref/source/dat/ossuary.des b/crawl-ref/source/dat/ossuary.des index f0e5e18580..89d9465766 100644 --- a/crawl-ref/source/dat/ossuary.des +++ b/crawl-ref/source/dat/ossuary.des @@ -476,11 +476,21 @@ TAGS: ossuary no_item_gen no_monster_gen ITEM: any scroll / any potion MONS: kobold skeleton / goblin skeleton / gnoll skeleton / \ orc skeleton +MONS: mummy # Loot: 10-19 +# Some random content. +SHUFFLE: Bb/Zz, Dd/Ww, Ee/Vv +SUBST: Z=c, z=c, W=c, w=c, V=c, v=c +SUBST: B=X, D=X, E=X +NSUBST: b = 1:= / c +NSUBST: d = 1:= / c +NSUBST: e = 1:= / c +SUBST: X = x2 # Guaranteed 10 items and 10 mummies, two in hidden rooms. # 50/50 chance of loot for each of the eleven rats in the rat room. KITEM: x = any scroll / any potion KMONS: x = mummy +NSUBST: ' = 10:^ / *:. KFEAT: ^ = dart trap / arrow trap KITEM: y = any scroll w:5 / any potion w:5 / nothing w:10 KMONS: y = rat skeleton @@ -489,23 +499,23 @@ KMONS: y = rat skeleton MAP ccc cccxccccc - ccc....+.^c cccccc - cc.1.ccccc.c cc.+^.cc - cc..ccc cc+cc cc..cc..cc - cc.^cc cc...ccccc..cccc.1ccc -ccc cc+cc c+.x.+...1.cc cc...cc -cAccc...cc cc...cccxccc ccc.cc -c..+.^1.+c cc+cc ccc ccc cc+cc -c<ccc...cc cc..cc cccccxccc...c -ccc cc+cc cc^1=cc cc..+.^..+.x.+ - c.cc cc..cc.cc c.1ccccccc...c - c^cc cc..cccc.ccc.cc cc+cc - c..cccc+cc cc.cc+cc ccc - cc1.cc...cccccc=yyyccccc - cc.+..x..+...+yyyyy+.^cc - cccc...cccxcccyyyccc..cc - cc=cc ccc cc+cc cc1.ccccc - cxc ccc cc...=xc + ccc''''+''c cccccc + cc'''ccccc'c cc'+.1cc + cc1'ccc cc+cc cc''cc''cc + cc..cc cc...ccccc''cccc''ccc +ccc cc+cc c+.x.+.1'''cc cc'''cc +cAccc'''cc ccb...cccxccc cecc'cc +c..+..1'+c cBcc+cc ccc cceEec+cc +c<ccc'''cc ccb''cc cccccxccc...ccc +ccc cc+cc cc''=cccccc''+''''+.x.+Xc + c.cc cc1'cc'cdDd.1ccccccc...ccc + c1cc cc..cccc'cdd.cc cc+cc + c''cccc+cc cc'cc+cc cXc + cc''cc...cccccc=yyyccccc ccc + cc'+..x..+'''+yyyyy+.1cc + cccc...cccxcccyyyccc''cc + cc=cc ccc cc+cc cc''ccccc + cxc ccc cc'''+Xc ccc ccccccc ENDMAP diff --git a/crawl-ref/source/dat/shoals.des b/crawl-ref/source/dat/shoals.des index 4983347ecd..2b52eb6ade 100644 --- a/crawl-ref/source/dat/shoals.des +++ b/crawl-ref/source/dat/shoals.des @@ -243,13 +243,13 @@ SHUFFLE: ABCD SUBST: A:x, B:x, C:x=, D=+ LROCKTILE: wall_vines MAP -.xxCxx. + xxCxx xx...xx x.....x B..O..D x.....x xx...xx -.xxAxx + xxAxx ENDMAP ################################################################################ @@ -262,11 +262,11 @@ TAGS: allow_dup water_ok shoal no_dump SHUFFLE: ABCD SUBST: A:x, B:x, C:x=, D=+ MAP -.xxCxx. + xxCxx xx...xx x.....x B..|..D x.....x xx...xx -.xxAxx + xxAxx ENDMAP diff --git a/crawl-ref/source/dbg-util.cc b/crawl-ref/source/dbg-util.cc index 57bf3b1b37..84e0f87a0e 100644 --- a/crawl-ref/source/dbg-util.cc +++ b/crawl-ref/source/dbg-util.cc @@ -129,12 +129,15 @@ void debug_dump_levgen() else { const CrawlHashTable &vaults = props[TEMP_VAULTS_KEY].get_table(); - CrawlHashTable::const_iterator i = vaults.begin(); - - for (; i != vaults.end(); ++i) + if (!vaults.empty()) { - mprf(" %s: %s", i->first.c_str(), - i->second.get_string().c_str()); + CrawlHashTable::const_iterator i = vaults.begin(); + + for (; i != vaults.end(); ++i) + { + mprf(" %s: %s", i->first.c_str(), + i->second.get_string().c_str()); + } } } mpr(""); @@ -413,5 +416,3 @@ int debug_cap_stat(int stat) stat > 127 ? 127 : stat); } - - diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc index a3a6ca6d5d..56c166fdec 100644 --- a/crawl-ref/source/delay.cc +++ b/crawl-ref/source/delay.cc @@ -1564,7 +1564,7 @@ void armour_wear_effects(const int item_slot) use_artefact(arm, &show_msgs, melded); } - if (item_cursed(arm) && !melded) + if (arm.cursed() && !melded) { mpr("Oops, that feels deathly cold."); learned_something_new(TUT_YOU_CURSED); diff --git a/crawl-ref/source/dgn-shoals.cc b/crawl-ref/source/dgn-shoals.cc new file mode 100644 index 0000000000..da0070239e --- /dev/null +++ b/crawl-ref/source/dgn-shoals.cc @@ -0,0 +1,692 @@ +#include "AppHdr.h" + +#include "branch.h" +#include "coord.h" +#include "coordit.h" +#include "dungeon.h" +#include "dgn-shoals.h" +#include "env.h" +#include "items.h" +#include "maps.h" +#include "mon-place.h" +#include "mon-util.h" +#include "random.h" +#include "terrain.h" + +#include <algorithm> +#include <vector> +#include <cmath> + +const char *ENVP_SHOALS_TIDE_KEY = "shoals-tide-height"; +const char *ENVP_SHOALS_TIDE_VEL = "shoals-tide-velocity"; + +inline short &shoals_heights(const coord_def &c) +{ + return (*env.heightmap)(c); +} + +static std::vector<coord_def> _shoals_islands; + +const int ISLAND_COLLIDE_DIST2 = 5 * 5; +const int N_PERTURB_ISLAND_CENTER = 50; +const int ISLAND_CENTER_RADIUS_LOW = 3; +const int ISLAND_CENTER_RADIUS_HIGH = 10; + +const int N_PERTURB_OFFSET_LOW = 25; +const int N_PERTURB_OFFSET_HIGH = 45; +const int PERTURB_OFFSET_RADIUS_LOW = 2; +const int PERTURB_OFFSET_RADIUS_HIGH = 7; + +const int _shoals_margin = 6; + +enum shoals_height_thresholds +{ + SHT_UNDEFINED = -10000, + SHT_STONE = 230, + SHT_ROCK = 170, + SHT_FLOOR = 0, + SHT_SHALLOW_WATER = -14 +}; + +enum tide_direction +{ + TIDE_RISING, + TIDE_FALLING +}; + +static tide_direction _shoals_tide_direction; + +static double _to_radians(int degrees) +{ + return degrees * M_PI / 180; +} + +static dungeon_feature_type _shoals_feature_by_height(int height) +{ + return height >= SHT_STONE ? DNGN_STONE_WALL : + height >= SHT_ROCK? DNGN_ROCK_WALL : + height >= SHT_FLOOR? DNGN_FLOOR : + height >= SHT_SHALLOW_WATER? DNGN_SHALLOW_WATER + : DNGN_DEEP_WATER; +} + +static dungeon_feature_type _shoals_feature_at(const coord_def &c) +{ + const int height = shoals_heights(c); + return _shoals_feature_by_height(height); +} + +static void _shoals_init_heights() +{ + env.heightmap.reset(new grid_heightmap); + for (rectangle_iterator ri(0); ri; ++ri) + shoals_heights(*ri) = SHT_SHALLOW_WATER - 3; +} + +static double _angle_fuzz() +{ + double fuzz = _to_radians(random2(15)); + return coinflip()? fuzz : -fuzz; +} + +static coord_def _random_point_from(const coord_def &c, int radius, + int directed_angle = -1) +{ + const double directed_radians( + directed_angle == -1? 0.0 : _to_radians(directed_angle)); + int attempts = 70; + while (attempts-- > 0) + { + const double angle = + directed_angle == -1? _to_radians(random2(360)) + : ((coinflip()? directed_radians : -directed_radians) + + _angle_fuzz()); + coord_def res = c + coord_def(radius * cos(angle), + radius * sin(angle)); + if (res.x >= _shoals_margin && res.x < GXM - _shoals_margin + && res.y >= _shoals_margin && res.y < GYM - _shoals_margin) + { + return res; + } + } + return coord_def(); +} + +static coord_def _random_point(int offset = 0) +{ + return coord_def(random_range(offset, GXM - offset - 1), + random_range(offset, GYM - offset - 1)); +} + +static void _shoals_island_center(const coord_def &c, int n_perturb, int radius, + int bounce_low, int bounce_high) +{ + for (int i = 0; i < n_perturb; ++i) { + coord_def p = _random_point_from(c, random2(1 + radius)); + if (!p.origin()) + shoals_heights(p) += random_range(bounce_low, bounce_high); + } +} + +static coord_def _shoals_pick_island_spot() +{ + coord_def c; + for (int i = 0; i < 15; ++i) + { + c = _random_point(_shoals_margin * 2); + + bool collides = false; + for (int j = 0, size = _shoals_islands.size(); j < size; ++j) + { + const coord_def island = _shoals_islands[j]; + const coord_def dist = island - c; + if (dist.abs() < ISLAND_COLLIDE_DIST2) + { + collides = true; + break; + } + } + if (!collides) + break; + } + _shoals_islands.push_back(c); + return c; +} + +static void _shoals_build_island() +{ + coord_def c = _shoals_pick_island_spot(); + _shoals_island_center(c, N_PERTURB_ISLAND_CENTER, + random_range(ISLAND_CENTER_RADIUS_LOW, + ISLAND_CENTER_RADIUS_HIGH), + 40, 60); + const int additional_heights = random2(4); + for (int i = 0; i < additional_heights; ++i) { + const int addition_offset = random_range(2, 10); + + coord_def offsetC = _random_point_from(c, addition_offset); + if (!offsetC.origin()) + _shoals_island_center(offsetC, random_range(N_PERTURB_OFFSET_LOW, + N_PERTURB_OFFSET_HIGH), + random_range(PERTURB_OFFSET_RADIUS_LOW, + PERTURB_OFFSET_RADIUS_HIGH), + 25, 35); + } +} + +static void _shoals_init_islands(int depth) +{ + const int nislands = 20 - depth * 2; + for (int i = 0; i < nislands; ++i) + _shoals_build_island(); +} + +// Cliffs are usually constructed in shallow water adjacent to deep +// water (for effect). +static void _shoals_build_cliff() +{ + coord_def cliffc = _random_point(_shoals_margin * 2); + if (in_bounds(cliffc)) + { + const int length = random_range(6, 15); + double angle = _to_radians(random2(360)); + for (int i = 0; i < length; i += 3) + { + int distance = i - length / 2; + coord_def place = cliffc + coord_def(distance * cos(angle), + distance * sin(angle)); + coord_def fuzz = coord_def(random_range(-2, 2), + random_range(-2, 2)); + place += fuzz; + _shoals_island_center(place, random_range(40, 60), 3, + 100, 130); + } + } +} + +static void _shoals_cliffs() +{ + const int ncliffs = random_range(0, 6, 2); + for (int i = 0; i < ncliffs; ++i) + _shoals_build_cliff(); +} + +static void _shoals_smooth_at(const coord_def &c, int radius, + int max_height = SHT_UNDEFINED) +{ + const int height = shoals_heights(c); + if (max_height != SHT_UNDEFINED && height > max_height) + return; + + int max_delta = radius * radius * 2 + 2; + int divisor = 0; + int total = 0; + for (int y = c.y - radius; y <= c.y + radius; ++y) { + for (int x = c.x - radius; x <= c.x + radius; ++x) { + const coord_def p(x, y); + if (!in_bounds(p)) + continue; + const int nheight = shoals_heights(p); + if (max_height != SHT_UNDEFINED && nheight > max_height) + continue; + const coord_def off = c - p; + int weight = max_delta - off.abs(); + divisor += weight; + total += nheight * weight; + } + } + shoals_heights(c) = total / divisor; +} + +static void _shoals_smooth() +{ + for (rectangle_iterator ri(0); ri; ++ri) + _shoals_smooth_at(*ri, 1); +} + +static void _shoals_smooth_water() +{ + for (rectangle_iterator ri(0); ri; ++ri) + _shoals_smooth_at(*ri, 1, SHT_SHALLOW_WATER - 1); +} + +static void _shoals_apply_level() +{ + for (rectangle_iterator ri(1); ri; ++ri) + grd(*ri) = _shoals_feature_at(*ri); +} + +static bool _has_adjacent_feat(coord_def c, dungeon_feature_type feat) +{ + for (adjacent_iterator ai(c); ai; ++ai) + if (grd(*ai) == feat) + return true; + return false; +} + +// Returns all points in deep water with an adjacent square in shallow water. +static std::vector<coord_def> _shoals_water_depth_change_points() +{ + std::vector<coord_def> points; + for (rectangle_iterator ri(1); ri; ++ri) + { + coord_def c(*ri); + if (grd(c) == DNGN_DEEP_WATER + && _has_adjacent_feat(c, DNGN_SHALLOW_WATER)) + points.push_back(c); + } + return points; +} + +static inline void _shoals_deepen_water_at(coord_def p, int distance) +{ + shoals_heights(p) -= distance * 7; +} + +static void _shoals_deepen_water() +{ + std::vector<coord_def> pages[2]; + int current_page = 0; + pages[current_page] = _shoals_water_depth_change_points(); + FixedArray<bool, GXM, GYM> seen_points(false); + + for (int i = 0, size = pages[current_page].size(); i < size; ++i) + seen_points(pages[current_page][i]) = true; + + int distance = 0; + while (!pages[current_page].empty()) + { + const int next_page = !current_page; + std::vector<coord_def> &cpage(pages[current_page]); + std::vector<coord_def> &npage(pages[next_page]); + for (int i = 0, size = cpage.size(); i < size; ++i) + { + coord_def c(cpage[i]); + if (distance) + _shoals_deepen_water_at(c, distance); + + for (adjacent_iterator ai(c); ai; ++ai) + { + coord_def adj(*ai); + if (!seen_points(adj) && grd(adj) == DNGN_DEEP_WATER) + { + npage.push_back(adj); + seen_points(adj) = true; + } + } + } + cpage.clear(); + current_page = next_page; + distance++; + } +} + +static coord_def _pick_shoals_island() +{ + const int lucky_island = random2(_shoals_islands.size()); + const coord_def c = _shoals_islands[lucky_island]; + _shoals_islands.erase(_shoals_islands.begin() + lucky_island); + return c; +} + +struct point_sort_distance_from +{ + coord_def bad_place; + point_sort_distance_from(coord_def c) : bad_place(c) { } + bool operator () (coord_def a, coord_def b) const + { + const int dista = (a - bad_place).abs(), distb = (b - bad_place).abs(); + return dista >= distb; + } +}; + +static coord_def _pick_shoals_island_distant_from(coord_def bad_place) +{ + ASSERT(!_shoals_islands.empty()); + + std::sort(_shoals_islands.begin(), _shoals_islands.end(), + point_sort_distance_from(bad_place)); + const int top_picks = std::min(4, int(_shoals_islands.size())); + const int choice = random2(top_picks); + coord_def chosen = _shoals_islands[choice]; + _shoals_islands.erase(_shoals_islands.begin() + choice); + return chosen; +} + +static void _shoals_furniture(int margin) +{ + if (at_branch_bottom()) + { + const coord_def c = _pick_shoals_island(); + // Put all the stairs on one island. + grd(c) = DNGN_STONE_STAIRS_UP_I; + grd(c + coord_def(1, 0)) = DNGN_STONE_STAIRS_UP_II; + grd(c - coord_def(1, 0)) = DNGN_STONE_STAIRS_UP_III; + + const coord_def p = _pick_shoals_island_distant_from(c); + // Place the rune + const map_def *vault = random_map_for_tag("shoal_rune"); + dgn_place_map(vault, false, true, p); + + const int nhuts = std::min(8, int(_shoals_islands.size())); + for (int i = 2; i < nhuts; ++i) + { + // Place (non-rune) minivaults on the other islands + do + vault = random_map_for_tag("shoal"); + while (!vault); + dgn_place_map(vault, false, true, _pick_shoals_island()); + } + } + else + { + // Place stairs randomly. No elevators. + for (int i = 0; i < 3; ++i) + { + int x, y; + do + { + x = margin + random2(GXM - 2*margin); + y = margin + random2(GYM - 2*margin); + } + while (grd[x][y] != DNGN_FLOOR); + + grd[x][y] + = static_cast<dungeon_feature_type>(DNGN_STONE_STAIRS_DOWN_I + i); + + do + { + x = margin + random2(GXM - 2*margin); + y = margin + random2(GYM - 2*margin); + } + while (grd[x][y] != DNGN_FLOOR); + + grd[x][y] + = static_cast<dungeon_feature_type>(DNGN_STONE_STAIRS_UP_I + i); + } + } +} + +static void _shoals_deepen_edges() +{ + const int edge = 2; + // Water of the edge of the screen is too deep to be exposed by tides. + for (int y = 1; y < GYM - 2; ++y) + { + for (int x = 1; x <= edge; ++x) + { + shoals_heights(coord_def(x, y)) -= 800; + shoals_heights(coord_def(GXM - 1 - x, y)) -= 800; + } + } + for (int x = 1; x < GXM - 2; ++x) + { + for (int y = 1; y <= edge; ++y) + { + shoals_heights(coord_def(x, y)) -= 800; + shoals_heights(coord_def(x, GYM - 1 - y)) -= 800; + } + } +} + +void prepare_shoals(int level_number) +{ + dgn_Build_Method += make_stringf(" shoals+ [%d]", level_number); + dgn_Layout_Type = "shoals"; + + const int shoals_depth = level_id::current().depth - 1; + dgn_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, DNGN_OPEN_SEA); + _shoals_init_heights(); + _shoals_islands.clear(); + _shoals_init_islands(shoals_depth); + _shoals_cliffs(); + _shoals_smooth(); + _shoals_apply_level(); + _shoals_deepen_water(); + _shoals_deepen_edges(); + _shoals_smooth_water(); + _shoals_furniture(_shoals_margin); +} + +// The raw tide height / TIDE_MULTIPLIER is the actual tide height. The higher +// the tide multiplier, the slower the tide advances and recedes. A multiplier +// of X implies that the tide will advance visibly about once in X turns. +const int TIDE_MULTIPLIER = 30; + +const int LOW_TIDE = -18 * TIDE_MULTIPLIER; +const int HIGH_TIDE = 25 * TIDE_MULTIPLIER; +const int TIDE_DECEL_MARGIN = 8; +const int START_TIDE_RISE = 2; + +static void _shoals_run_tide(int &tide, int &acc) +{ + tide += acc; + tide = std::max(std::min(tide, HIGH_TIDE), LOW_TIDE); + if ((tide == HIGH_TIDE && acc > 0) + || (tide == LOW_TIDE && acc < 0)) + acc = -acc; + bool in_decel_margin = + (abs(tide - HIGH_TIDE) < TIDE_DECEL_MARGIN) + || (abs(tide - LOW_TIDE) < TIDE_DECEL_MARGIN); + if ((abs(acc) == 2) == in_decel_margin) + acc = in_decel_margin? acc / 2 : acc * 2; +} + +static coord_def _shoals_escape_place_from(coord_def bad_place, + bool monster_free) +{ + int best_height = -1000; + coord_def chosen; + for (adjacent_iterator ai(bad_place); ai; ++ai) + { + coord_def p(*ai); + const dungeon_feature_type feat(grd(p)); + if (!feat_is_solid(feat) && !feat_destroys_items(feat) + && (!monster_free || !actor_at(p))) + { + if (best_height == -1000 || shoals_heights(p) > best_height) + { + best_height = shoals_heights(p); + chosen = p; + } + } + } + return chosen; +} + +static bool _shoals_tide_sweep_items_clear(coord_def c) +{ + int link = igrd(c); + if (link == NON_ITEM) + return true; + + const coord_def target(_shoals_escape_place_from(c, false)); + if (target.origin()) + return false; + + move_item_stack_to_grid(c, target); + return true; +} + +static bool _shoals_tide_sweep_actors_clear(coord_def c) +{ + actor *victim = actor_at(c); + if (!victim || victim->airborne() || victim->swimming()) + return true; + + if (victim->atype() == ACT_MONSTER) + { + monsters *mvictim = dynamic_cast<monsters *>(victim); + // Plants and statues cannot be moved away; the tide cannot + // drown them. + if (mons_class_is_stationary(mvictim->type)) + return false; + + // If the monster doesn't need help, move along. + if (monster_habitable_grid(mvictim, DNGN_DEEP_WATER)) + return true; + } + coord_def evacuation_point(_shoals_escape_place_from(c, true)); + // The tide moves on even if we cannot evacuate the tile! + if (!evacuation_point.origin()) + victim->move_to_pos(evacuation_point); + return true; +} + +// The tide will attempt to push items and non-water-capable monsters to +// adjacent squares. +static bool _shoals_tide_sweep_clear(coord_def c) +{ + return _shoals_tide_sweep_items_clear(c) + && _shoals_tide_sweep_actors_clear(c); +} + +static void _shoals_apply_tide_feature_at(coord_def c, + dungeon_feature_type feat) +{ + if (feat == DNGN_DEEP_WATER && !_shoals_tide_sweep_clear(c)) + feat = DNGN_SHALLOW_WATER; + + const dungeon_feature_type current_feat = grd(c); + if (feat == current_feat) + return; + + dungeon_terrain_changed(c, feat, true, false, true); +} + +static int _shoals_feature_height(dungeon_feature_type feat) +{ + switch (feat) + { + case DNGN_FLOOR: + return SHT_FLOOR; + case DNGN_SHALLOW_WATER: + return SHT_SHALLOW_WATER; + case DNGN_DEEP_WATER: + return SHT_SHALLOW_WATER - 1; + default: + return 0; + } +} + +// Determines if the +static tide_direction _shoals_feature_tide_height_change( + dungeon_feature_type oldfeat, + dungeon_feature_type newfeat) +{ + const int height_delta = + _shoals_feature_height(newfeat) - _shoals_feature_height(oldfeat); + // If the apparent height of the new feature is greater (floor vs water), + // the tide is receding. + return height_delta < 0? TIDE_RISING : TIDE_FALLING; +} + +static void _shoals_apply_tide_at(coord_def c, int tide) +{ + const int effective_height = shoals_heights(c) - tide; + dungeon_feature_type newfeat = + _shoals_feature_by_height(effective_height); + // Make sure we're not sprouting new walls. + if (feat_is_wall(newfeat)) + newfeat = DNGN_FLOOR; + const dungeon_feature_type oldfeat = grd(c); + + if (oldfeat == newfeat + || (_shoals_feature_tide_height_change(oldfeat, newfeat) != + _shoals_tide_direction)) + { + return; + } + + _shoals_apply_tide_feature_at(c, newfeat); +} + +static void _shoals_apply_tide(int tide) +{ + std::vector<coord_def> pages[2]; + int current_page = 0; + + // Start from corners of the map. + pages[current_page].push_back(coord_def(1,1)); + pages[current_page].push_back(coord_def(GXM - 2, 1)); + pages[current_page].push_back(coord_def(1, GYM - 2)); + pages[current_page].push_back(coord_def(GXM - 2, GYM - 2)); + + FixedArray<bool, GXM, GYM> seen_points(false); + + while (!pages[current_page].empty()) + { + int next_page = !current_page; + std::vector<coord_def> &cpage(pages[current_page]); + std::vector<coord_def> &npage(pages[next_page]); + + for (int i = 0, size = cpage.size(); i < size; ++i) + { + coord_def c(cpage[i]); + const bool was_wet(feat_is_water(grd(c))); + seen_points(c) = true; + _shoals_apply_tide_at(c, tide); + + // Only wet squares can propagate the tide onwards. + if (was_wet) + { + for (adjacent_iterator ai(c); ai; ++ai) + { + coord_def adj(*ai); + if (!in_bounds(adj)) + continue; + if (!seen_points(adj)) + { + const dungeon_feature_type feat = grd(adj); + if (feat_is_water(feat) || feat == DNGN_FLOOR) + { + npage.push_back(adj); + seen_points(adj) = true; + } + } + } + } + } + + cpage.clear(); + current_page = next_page; + } +} + +static void _shoals_init_tide() +{ + if (!env.properties.exists(ENVP_SHOALS_TIDE_KEY)) + { + env.properties[ENVP_SHOALS_TIDE_KEY] = short(0); + env.properties[ENVP_SHOALS_TIDE_VEL] = short(2); + } +} + +void shoals_apply_tides(int turns_elapsed) +{ + if (!player_in_branch(BRANCH_SHOALS) || !turns_elapsed + || !env.heightmap.get()) + { + return; + } + + const int TIDE_UNIT = HIGH_TIDE - LOW_TIDE; + // If we've been gone a long time, eliminate some unnecessary math. + if (turns_elapsed > TIDE_UNIT * 2) + turns_elapsed = turns_elapsed % TIDE_UNIT + TIDE_UNIT; + + _shoals_init_tide(); + int tide = env.properties[ENVP_SHOALS_TIDE_KEY].get_short(); + int acc = env.properties[ENVP_SHOALS_TIDE_VEL].get_short(); + const int old_tide = tide; + while (turns_elapsed-- > 0) + _shoals_run_tide(tide, acc); + env.properties[ENVP_SHOALS_TIDE_KEY] = short(tide); + env.properties[ENVP_SHOALS_TIDE_VEL] = short(acc); + if (old_tide / TIDE_MULTIPLIER != tide / TIDE_MULTIPLIER) + { + _shoals_tide_direction = + tide > old_tide ? TIDE_RISING : TIDE_FALLING; + _shoals_apply_tide(tide / TIDE_MULTIPLIER); + } +} diff --git a/crawl-ref/source/dgn-shoals.h b/crawl-ref/source/dgn-shoals.h new file mode 100644 index 0000000000..aae09ff653 --- /dev/null +++ b/crawl-ref/source/dgn-shoals.h @@ -0,0 +1,7 @@ +#ifndef DGN_SHOALS_H +#define DGN_SHOALS_H + +void prepare_shoals(int level_number); +void shoals_apply_tides(int turns_elapsed); + +#endif diff --git a/crawl-ref/source/directn.cc b/crawl-ref/source/directn.cc index 518d21b23c..3b0a1262b3 100644 --- a/crawl-ref/source/directn.cc +++ b/crawl-ref/source/directn.cc @@ -3418,10 +3418,9 @@ static void _describe_cell(const coord_def& where, bool in_range) if (env.cgrid(where) != EMPTY_CLOUD) { const int cloud_inspected = env.cgrid(where); - const cloud_type ctype = (cloud_type) env.cloud[cloud_inspected].type; mprf(MSGCH_EXAMINE, "There is a cloud of %s here.", - cloud_name(ctype).c_str()); + cloud_name(cloud_inspected).c_str()); cloud_described = true; } @@ -3468,14 +3467,19 @@ static void _describe_cell(const coord_def& where, bool in_range) marker = " (" + desc + ")"; } const std::string traveldest = _stair_destination_description(where); + std::string height_desc; + if (env.heightmap.get()) + height_desc = make_stringf(" (height: %d)", (*env.heightmap)(where)); const dungeon_feature_type feat = grd(where); - mprf(MSGCH_DIAGNOSTICS, "(%d,%d): %s - %s (%d/%s)%s%s", where.x, where.y, + mprf(MSGCH_DIAGNOSTICS, "(%d,%d): %s - %s (%d/%s)%s%s%s", + where.x, where.y, stringize_glyph(get_screen_glyph(where)).c_str(), feature_desc.c_str(), feat, dungeon_feature_name(feat), marker.c_str(), - traveldest.c_str()); + traveldest.c_str(), + height_desc.c_str()); #else if (Tutorial.tutorial_left && tutorial_pos_interesting(where.x, where.y)) { diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc index bef185979e..89e643a68b 100644 --- a/crawl-ref/source/dungeon.cc +++ b/crawl-ref/source/dungeon.cc @@ -23,6 +23,7 @@ #include "coord.h" #include "coordit.h" #include "defines.h" +#include "dgn-shoals.h" #include "effects.h" #include "env.h" #include "enum.h" @@ -112,18 +113,6 @@ static bool _make_box(int room_x1, int room_y1, int room_x2, int room_y2, dungeon_feature_type floor=DNGN_UNSEEN, dungeon_feature_type wall=DNGN_UNSEEN, dungeon_feature_type avoid=DNGN_UNSEEN); -static void _replace_area(const coord_def& p1, const coord_def& p2, - dungeon_feature_type replace, - dungeon_feature_type feature, - unsigned mmask = 0, bool needs_update = false); -static void _replace_area(int sx, int sy, int ex, int ey, - dungeon_feature_type replace, - dungeon_feature_type feature, - unsigned mmask = 0, bool needs_update = false) -{ - _replace_area( coord_def(sx, sy), coord_def(ex, ey), - replace, feature, mmask, needs_update ); -} static builder_rc_type _builder_by_type(int level_number, char level_type); static builder_rc_type _builder_by_branch(int level_number); @@ -145,7 +134,6 @@ static int _place_uniques(int level_number, char level_type); static void _place_traps( int level_number ); static void _place_fog_machines( int level_number ); static void _prepare_swamp(); -static void _prepare_shoals( int level_number ); static void _prepare_water( int level_number ); static void _check_doors(); static void _hide_doors(); @@ -835,7 +823,17 @@ void dgn_register_place(const vault_placement &place, bool register_vault) dgn_register_vault(place.map); if (!place.map.has_tag("layout")) - _mask_vault(place, MMT_VAULT | MMT_NO_DOOR); + { + if (place.map.orient == MAP_ENCOMPASS) + { + for (rectangle_iterator ri(0); ri; ++ri) + dgn_Map_Mask(*ri) |= MMT_VAULT | MMT_NO_DOOR; + } + else + { + _mask_vault(place, MMT_VAULT | MMT_NO_DOOR); + } + } if (place.map.has_tag("no_monster_gen")) _mask_vault(place, MMT_NO_MONS); @@ -987,6 +985,7 @@ void dgn_reset_level() // Forget level properties. env.properties.clear(); + env.heightmap.reset(NULL); // Set up containers for storing some level generation info. env.properties[LEVEL_VAULTS_KEY].new_table(); @@ -1104,8 +1103,7 @@ static void _build_layout_skeleton(int level_number, int level_type, if (!dgn_level_vetoed && skip_build == BUILD_CONTINUE) { - if (!player_in_branch(BRANCH_SHOALS)) - skip_build = _builder_basic(level_number); + skip_build = _builder_basic(level_number); if (!dgn_level_vetoed && skip_build == BUILD_CONTINUE) _builder_extras(level_number, level_type); } @@ -1172,8 +1170,8 @@ static void _fixup_walls() else if (player_in_branch(BRANCH_CRYPT)) vault_wall = DNGN_STONE_WALL; - _replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, vault_wall, - MMT_NO_WALL); + dgn_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, vault_wall, + MMT_NO_WALL); } } @@ -1798,8 +1796,6 @@ static void _build_dungeon_level(int level_number, int level_type) // Time to make the swamp or shoals {dlb}: if (player_in_branch(BRANCH_SWAMP)) _prepare_swamp(); - else if (player_in_branch(BRANCH_SHOALS)) - _prepare_shoals(level_number); if (dgn_level_vetoed) return; @@ -1810,9 +1806,10 @@ static void _build_dungeon_level(int level_number, int level_type) _hide_doors(); // Change pre-rock to rock, and pre-floor to floor. - _replace_area(0, 0, GXM-1, GYM-1, DNGN_BUILDER_SPECIAL_WALL, - DNGN_ROCK_WALL); - _replace_area(0, 0, GXM-1, GYM-1, DNGN_BUILDER_SPECIAL_FLOOR, DNGN_FLOOR); + dgn_replace_area(0, 0, GXM-1, GYM-1, DNGN_BUILDER_SPECIAL_WALL, + DNGN_ROCK_WALL); + dgn_replace_area(0, 0, GXM-1, GYM-1, DNGN_BUILDER_SPECIAL_FLOOR, + DNGN_FLOOR); const unsigned nvaults = Level_Vaults.size(); @@ -2003,19 +2000,6 @@ static void _hide_doors() } } -// Places a randomised ellipse with centre (x, y) and half axes a and b. -static void _place_ellipse(int x, int y, int a, int b, - dungeon_feature_type feat, int margin) -{ - for (int i = std::max(x-a, margin); i < std::min(x+a, GXM-margin); ++i) - for (int j = std::max(y-b, margin); j < std::min(y+b, GYM-margin); ++j) - if ( (x-i)*(x-i)*b*b + (y-j)*(y-j)*a*a - <= a*a*b*b/2 + roll_dice(2, a*a*b*b)/4 + random2(3) ) - { - grd[i][j] = feat; - } -} - int count_feature_in_box(int x0, int y0, int x1, int y1, dungeon_feature_type feat) { @@ -2042,18 +2026,6 @@ int count_neighbours(int x, int y, dungeon_feature_type feat) return count_feature_in_box(x-1, y-1, x+2, y+2, feat); } -static void _replace_in_grid(int x1, int y1, int x2, int y2, - dungeon_feature_type oldfeat, - dungeon_feature_type newfeat) -{ - for (int x = x1; x < x2; ++x) - for (int y = y1; y < y2; ++y) - { - if (grd[x][y] == oldfeat) - grd[x][y] = newfeat; - } -} - static void _connected_flood(int margin, int i, int j, bool taken[GXM][GYM]) { if (i < margin || i >= GXM - margin || j < margin || j >= GYM - margin @@ -2068,301 +2040,6 @@ static void _connected_flood(int margin, int i, int j, bool taken[GXM][GYM]) _connected_flood(margin, i + idelta, j + jdelta, taken); } -// Yes, yes, this can probably use travel to avoid duplicating code. -static int _count_connected(int margin) -{ - bool taken[GXM][GYM]; - - for (int i = margin; i < GXM - margin; ++i) - for (int j = margin; j < GYM - margin; ++j) - taken[i][j] = feat_is_water(grd[i][j]); - - int count = 0; - - for (int i = margin; i < GXM - margin; ++i) - for (int j = margin; j < GYM - margin; ++j) - if (!taken[i][j]) - { - ++count; - _connected_flood(margin,i,j,taken); - } - - return count; -} - -static void _place_base_islands(int margin, int num_islands, int estradius, - coord_def centres[10]) -{ - for (int i = 0; i < num_islands; ++i) - { - // smaller axis - int b = (2 * estradius + roll_dice(3, estradius)) / 4; - b = std::max(b, 4); - b = std::min(b, (GYM - 2*margin - 2) / 2); - - int a = b + roll_dice(2, b)/3; // more wide than tall - a = std::min(a, (GXM - 2*margin - 2) / 2); - - int island_distance = estradius*estradius * (2 + num_islands/3); - - // Make sure an island fits into the map without getting - // cut off on the edges, i.e. leave margin + radius space - // at each side. - const int rnd_x_size = GXM - 2*(margin+a) - 1; - const int rnd_y_size = GYM - 2*(margin+b) - 1; - ASSERT(rnd_x_size > 0); - ASSERT(rnd_y_size > 0); - - bool centre_ok = false; - int tries = 1000; - do - { - centre_ok = true; - - centres[i].x = margin + a + random2(rnd_x_size); - centres[i].y = margin + b + random2(rnd_y_size); - - for (int j = 0; j < i; ++j) - { - // Calculate the distance from the centers of - // previous islands. - if (distance(centres[i].x, centres[i].y, - centres[j].x, centres[j].y) < island_distance) - { - centre_ok = false; - break; - } - } - if (island_distance && !one_chance_in(num_islands)) - --island_distance; - } - while (!centre_ok && --tries > 0); - -#ifdef DEBUG_DIAGNOSTICS - if (tries == 0) - mprf(MSGCH_ERROR, "Failure to place island #%d!", i); -#endif - // Eventually, place an ellipse around the new coordinate. - _place_ellipse( centres[i].x, centres[i].y, a, b, DNGN_FLOOR, margin); - } -} - -typedef std::pair<coord_def, dungeon_feature_type> located_feature; -typedef std::list<located_feature> located_feature_list; - -bool _is_critical_feature(dungeon_feature_type feat) -{ - return (feat == DNGN_ENTER_PORTAL_VAULT - || feat == DNGN_ENTER_LABYRINTH - || feat == DNGN_ENTER_SHOP); -} - -static located_feature_list _save_critical_features() -{ - located_feature_list result; - - for (rectangle_iterator ri(1); ri; ++ri) - if (_is_critical_feature(grd(*ri))) - result.push_back(located_feature(*ri, grd(*ri))); - - return result; -} - -static void _restore_critical_features(const located_feature_list& lfl) -{ - for (located_feature_list::const_iterator ci = lfl.begin(); - ci != lfl.end(); ++ci) - { - grd(ci->first) = ci->second; - } -} - -static void _prepare_shoals(int level_number) -{ - // Don't destroy portals, etc. - const located_feature_list lfl = _save_critical_features(); - - dgn_Build_Method += make_stringf(" shoals [%d]", level_number); - dgn_Layout_Type = "shoals"; - - // dpeg's algorithm. - // We could have just used spotty_level() and changed rock to - // water, but this is much cooler. Right? - const int margin = 6; - - int num_islands = player_branch_depth() + 1; - - if (at_branch_bottom()) - num_islands += random2(3); - - const int estradius = 50 / num_islands - (num_islands == 2 ? 5 : 0); - - int num_tries = 0; - coord_def centres[10]; - do - { - for (int x = margin; x < GXM-margin; ++x) - for (int y = margin; y < GYM-margin; ++y) - grd[x][y] = DNGN_DEEP_WATER; - - _place_base_islands(margin, num_islands, estradius, centres); - } - while (++num_tries < 100 && _count_connected(margin) != num_islands); - -#ifdef DEBUG_DIAGNOSTICS - mprf(MSGCH_DIAGNOSTICS, "Num tries: %d Connected components: %d", - num_tries, _count_connected(margin)); -#endif - - // Adding shallow water at deep water adjacent to floor. - // Randomisation: place shallow water if at least 1d(1d3) floor neighbours - for (int i = margin; i < GXM - margin; ++i) - for (int j = margin; j < GYM - margin; ++j) - if (grd[i][j] == DNGN_DEEP_WATER - && count_neighbours(i, j, DNGN_FLOOR) > random2(random2(3)+1)) - { - grd[i][j] = DNGN_SHALLOW_WATER; - } - - // Placing sandbanks. - for (int banks = 0; banks < 8; ++banks) - { - int xsize = 3 + random2(3); // random rectangle - int ysize = 3 + random2(3); - int xb = margin + 2 + random2(GXM - 2 * margin - xsize - 2); - int yb = margin + 2 + random2(GYM - 2 * margin - ysize - 2); - - bool ok_place = true; - for (int i = xb; i < xb + xsize; ++i) - for (int j = yb; j < yb + ysize; ++j) - { - if (grd[i][j] != DNGN_DEEP_WATER) - { - ok_place = false; - break; - } - } - - if (ok_place) - { - for (int i = xb; i < xb + xsize; ++i) - for (int j = yb; j < yb + ysize; ++j) - { - if (!one_chance_in(3)) - grd[i][j] = DNGN_SHALLOW_WATER; - } - } - } - - // Fractalisation - - // LAVA is a placeholder for cells which will become shallow water - // at the end of the current iteration. - // WATER_RESERVED is a placeholder for last iteration's generated water. - _replace_in_grid(margin, margin, GXM-margin, GYM-margin, - DNGN_SHALLOW_WATER, DNGN_WATER_RESERVED); - - for (int iteration = 0; iteration < 6; ++iteration) - { - for (int x = margin; x < GXM - margin; ++x) - for (int y = margin; y < GYM - margin; ++y) - if (grd[x][y] == DNGN_DEEP_WATER) - { - int badness = count_neighbours(x, y, DNGN_WATER_RESERVED); - if (random2(badness) >= 2 && coinflip()) - grd[x][y] = DNGN_LAVA; - } - - _replace_in_grid(margin, margin, GXM-margin, GYM-margin, - DNGN_LAVA, DNGN_WATER_RESERVED); - } - _replace_in_grid(margin, margin, GXM-margin, GYM-margin, - DNGN_WATER_RESERVED, DNGN_SHALLOW_WATER); - - // Turn rock walls into deep water and "open sea". This must happen - // before vaults are placed as those may contain rock as well. - const int margin2 = margin - 1; - _replace_in_grid(margin2, margin2, GXM-margin2, GYM-margin2, - DNGN_ROCK_WALL, DNGN_DEEP_WATER); - - _replace_area(0, 0, GXM-1, GYM-1, DNGN_BUILDER_SPECIAL_WALL, - DNGN_ROCK_WALL); - _replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, DNGN_OPEN_SEA); - - // Put important things back. - _restore_critical_features(lfl); - - if (at_branch_bottom()) - { - // Find an island to place the stairs up on. - int j; - const coord_def d1(1,0), d2(-1,0); - for (j = 0; j < num_islands; ++j) - { - if (!_is_critical_feature(grd(centres[j])) - && !_is_critical_feature(grd(centres[j] + d1)) - && !_is_critical_feature(grd(centres[j] + d2))) - { - break; - } - } - if (j == num_islands) - { - // Oh well. - j = 0; - mprf(MSGCH_ERROR, "Clobbering critical features: %d %d %d.", - grd(centres[j]), grd(centres[j] + d1), grd(centres[j] + d2)); - } - // Put all the stairs on one island. - grd[centres[j].x ][centres[j].y] = DNGN_STONE_STAIRS_UP_I; - grd[centres[j].x+1][centres[j].y] = DNGN_STONE_STAIRS_UP_II; - grd[centres[j].x-1][centres[j].y] = DNGN_STONE_STAIRS_UP_III; - - // Place the rune - const map_def *vault = random_map_for_tag("shoal_rune"); - _build_secondary_vault(level_number, vault, -1, false, true, - centres[1]); - - for (int i = 2; i < num_islands; ++i) - { - // Place (non-rune) minivaults on the other islands - do - vault = random_map_for_tag("shoal"); - while (!vault); - - _build_secondary_vault(level_number, vault, -1, false, true, - centres[i]); - } - } - else - { - // Place stairs randomly. No elevators. - for (int i = 0; i < 3; ++i) - { - int x, y; - do - { - x = margin + random2(GXM - 2*margin); - y = margin + random2(GYM - 2*margin); - } - while (grd[x][y] != DNGN_FLOOR); - - grd[x][y] - = static_cast<dungeon_feature_type>(DNGN_STONE_STAIRS_DOWN_I + i); - - do - { - x = margin + random2(GXM - 2*margin); - y = margin + random2(GYM - 2*margin); - } - while (grd[x][y] != DNGN_FLOOR); - - grd[x][y] - = static_cast<dungeon_feature_type>(DNGN_STONE_STAIRS_UP_I + i); - } - } -} - static void _prepare_swamp() { dgn_Build_Method += " swamp"; @@ -2631,7 +2308,8 @@ static void _portal_vault_level(int level_number) { // XXX: This is pretty hackish, I confess. if (vault->border_fill_type != DNGN_ROCK_WALL) - _replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, vault->border_fill_type); + dgn_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, + vault->border_fill_type); _ensure_vault_placed( _build_vaults(level_number, vault), true ); } @@ -2755,6 +2433,9 @@ static builder_rc_type _builder_by_branch(int level_number) spotty_level(false, iterations, false); return BUILD_SKIP; } + case BRANCH_SHOALS: + prepare_shoals(level_number); + return BUILD_SKIP; default: break; @@ -4056,8 +3737,8 @@ static void _build_rooms(const dgn_region_list &excluded, } const coord_def end = myroom.end(); - _replace_area(myroom.pos.x, myroom.pos.y, end.x, end.y, - DNGN_ROCK_WALL, DNGN_FLOOR); + dgn_replace_area(myroom.pos.x, myroom.pos.y, end.x, end.y, + DNGN_ROCK_WALL, DNGN_FLOOR); if (which_room > 0) { @@ -4421,7 +4102,7 @@ bool dgn_place_map(const map_def *mdef, bool clobber, bool make_no_exits, } } - dungeon_feature_type rune_subst = DNGN_FLOOR; + int rune_subst = -1; if (mdef->has_tag_suffix("_entry")) rune_subst = _dgn_find_rune_subst_tags(mdef->tags); did_map = _build_secondary_vault(you.your_level, mdef, rune_subst, @@ -4852,6 +4533,11 @@ static void _dgn_give_mon_spec_items(mons_spec &mspec, if (item_made != NON_ITEM && item_made != -1) { item_def &item(mitm[item_made]); + + // Mark items on summoned monsters as such. + if (mspec.abjuration_duration != 0) + item.flags |= ISFLAG_SUMMONED; + if (!mon.pickup_item(item, 0, true)) destroy_item(item_made, true); } @@ -5374,10 +5060,19 @@ bool seen_replace_feat(dungeon_feature_type old_feat, return (seen); } -static void _replace_area( const coord_def& p1, const coord_def& p2, - dungeon_feature_type replace, - dungeon_feature_type feature, unsigned mapmask, - bool needs_update) +void dgn_replace_area(int sx, int sy, int ex, int ey, + dungeon_feature_type replace, + dungeon_feature_type feature, + unsigned mmask, bool needs_update) +{ + dgn_replace_area( coord_def(sx, sy), coord_def(ex, ey), + replace, feature, mmask, needs_update ); +} + +void dgn_replace_area( const coord_def& p1, const coord_def& p2, + dungeon_feature_type replace, + dungeon_feature_type feature, unsigned mapmask, + bool needs_update) { for (rectangle_iterator ri(p1, p2); ri; ++ri) { @@ -6283,7 +5978,7 @@ static void _plan_main(int level_number, int force_plan) } if (one_chance_in(20)) - _replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, special_grid); + dgn_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, special_grid); } static bool _plan_1(int level_number) @@ -6364,7 +6059,8 @@ static bool _plan_4(char forbid_x1, char forbid_y1, char forbid_x2, : DNGN_METAL_WALL); // odds: 3 in 18 {dlb} } - _replace_area(10, 10, (GXM - 10), (GYM - 10), DNGN_ROCK_WALL, DNGN_FLOOR); + dgn_replace_area(10, 10, (GXM - 10), (GYM - 10), DNGN_ROCK_WALL, + DNGN_FLOOR); // replace_area can also be used to fill in: for (i = 0; i < number_boxes; i++) @@ -6410,7 +6106,7 @@ static bool _plan_4(char forbid_x1, char forbid_y1, char forbid_x2, temp_rand = random2(210); if (temp_rand > 71) // odds: 138 in 210 {dlb} - _replace_area(b1x, b1y, b2x, b2y, DNGN_FLOOR, drawing); + dgn_replace_area(b1x, b1y, b2x, b2y, DNGN_FLOOR, drawing); else // odds: 72 in 210 {dlb} _box_room(b1x, b2x - 1, b1y, b2y - 1, drawing); } @@ -7223,12 +6919,12 @@ static void _box_room(int bx1, int bx2, int by1, int by2, int temp_rand, new_doors, doors_placed; // Do top & bottom walls. - _replace_area(bx1, by1, bx2, by1, DNGN_FLOOR, wall_type); - _replace_area(bx1, by2, bx2, by2, DNGN_FLOOR, wall_type); + dgn_replace_area(bx1, by1, bx2, by1, DNGN_FLOOR, wall_type); + dgn_replace_area(bx1, by2, bx2, by2, DNGN_FLOOR, wall_type); // Do left & right walls. - _replace_area(bx1, by1+1, bx1, by2-1, DNGN_FLOOR, wall_type); - _replace_area(bx2, by1+1, bx2, by2-1, DNGN_FLOOR, wall_type); + dgn_replace_area(bx1, by1+1, bx1, by2-1, DNGN_FLOOR, wall_type); + dgn_replace_area(bx2, by1+1, bx2, by2-1, DNGN_FLOOR, wall_type); // Sometimes we have to place doors, or else we shut in other // buildings' doors. @@ -7466,8 +7162,8 @@ static void _big_room(int level_number) } // Make the big room. - _replace_area(sr.tl, sr.br, DNGN_ROCK_WALL, type_floor); - _replace_area(sr.tl, sr.br, DNGN_CLOSED_DOOR, type_floor); + dgn_replace_area(sr.tl, sr.br, DNGN_ROCK_WALL, type_floor); + dgn_replace_area(sr.tl, sr.br, DNGN_CLOSED_DOOR, type_floor); if (type_floor == DNGN_FLOOR) { @@ -7565,8 +7261,8 @@ static void _roguey_level(int level_number, spec_room &sr, bool make_stairs) for (i = 0; i < 25; i++) { - _replace_area( rox1[i], roy1[i], rox2[i], roy2[i], - DNGN_ROCK_WALL, DNGN_FLOOR ); + dgn_replace_area( rox1[i], roy1[i], rox2[i], roy2[i], + DNGN_ROCK_WALL, DNGN_FLOOR ); // Inner room? if (rox2[i] - rox1[i] > 5 && roy2[i] - roy1[i] > 5 @@ -7665,12 +7361,11 @@ static void _roguey_level(int level_number, spec_room &sr, bool make_stairs) // Is one of them a special room? const map_def *sroom = NULL; -#ifdef DEBUG_SPECIAL_ROOMS - if ((sroom = random_map_for_tag("special_room", true)) != NULL) -#else - if (one_chance_in(10) - && (sroom = random_map_for_tag("special_room", true)) != NULL) + if ( +#ifndef DEBUG_SPECIAL_ROOMS + one_chance_in(10) && #endif + (sroom = random_map_for_tag("special_room", true)) != NULL) { int spec_room_done = random2(25); @@ -7684,36 +7379,36 @@ static void _roguey_level(int level_number, spec_room &sr, bool make_stairs) // by something else (or put monsters in walls, etc...) // top - _replace_area(sr.tl.x-1, sr.tl.y-1, sr.br.x+1,sr.tl.y-1, - DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL); - _replace_area(sr.tl.x-1, sr.tl.y-1, sr.br.x+1,sr.tl.y-1, - DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR); - _replace_area(sr.tl.x-1, sr.tl.y-1, sr.br.x+1,sr.tl.y-1, - DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR); + dgn_replace_area(sr.tl.x-1, sr.tl.y-1, sr.br.x+1,sr.tl.y-1, + DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL); + dgn_replace_area(sr.tl.x-1, sr.tl.y-1, sr.br.x+1,sr.tl.y-1, + DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR); + dgn_replace_area(sr.tl.x-1, sr.tl.y-1, sr.br.x+1,sr.tl.y-1, + DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR); // bottom - _replace_area(sr.tl.x-1, sr.br.y+1, sr.br.x+1,sr.br.y+1, - DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL); - _replace_area(sr.tl.x-1, sr.br.y+1, sr.br.x+1,sr.br.y+1, - DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR); - _replace_area(sr.tl.x-1, sr.br.y+1, sr.br.x+1,sr.br.y+1, - DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR); + dgn_replace_area(sr.tl.x-1, sr.br.y+1, sr.br.x+1,sr.br.y+1, + DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL); + dgn_replace_area(sr.tl.x-1, sr.br.y+1, sr.br.x+1,sr.br.y+1, + DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR); + dgn_replace_area(sr.tl.x-1, sr.br.y+1, sr.br.x+1,sr.br.y+1, + DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR); // left - _replace_area(sr.tl.x-1, sr.tl.y-1, sr.tl.x-1, sr.br.y+1, - DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL); - _replace_area(sr.tl.x-1, sr.tl.y-1, sr.tl.x-1, sr.br.y+1, - DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR); - _replace_area(sr.tl.x-1, sr.tl.y-1, sr.tl.x-1, sr.br.y+1, - DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR); + dgn_replace_area(sr.tl.x-1, sr.tl.y-1, sr.tl.x-1, sr.br.y+1, + DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL); + dgn_replace_area(sr.tl.x-1, sr.tl.y-1, sr.tl.x-1, sr.br.y+1, + DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR); + dgn_replace_area(sr.tl.x-1, sr.tl.y-1, sr.tl.x-1, sr.br.y+1, + DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR); // right - _replace_area(sr.br.x+1, sr.tl.y-1, sr.br.x+1, sr.br.y+1, - DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL); - _replace_area(sr.br.x+1, sr.tl.y-1, sr.br.x+1, sr.br.y+1, - DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR); - _replace_area(sr.br.x+1, sr.tl.y-1, sr.br.x+1, sr.br.y+1, - DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR); + dgn_replace_area(sr.br.x+1, sr.tl.y-1, sr.br.x+1, sr.br.y+1, + DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL); + dgn_replace_area(sr.br.x+1, sr.tl.y-1, sr.br.x+1, sr.br.y+1, + DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR); + dgn_replace_area(sr.br.x+1, sr.tl.y-1, sr.br.x+1, sr.br.y+1, + DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR); } if (!make_stairs) diff --git a/crawl-ref/source/dungeon.h b/crawl-ref/source/dungeon.h index 590a91b897..bc140c3f2e 100644 --- a/crawl-ref/source/dungeon.h +++ b/crawl-ref/source/dungeon.h @@ -163,6 +163,7 @@ public: extern bool Generating_Level; extern std::string dgn_Layout_Type; +extern std::string dgn_Build_Method; extern std::set<std::string> Level_Unique_Maps; extern std::set<std::string> Level_Unique_Tags; @@ -187,6 +188,7 @@ void dgn_set_grid_colour_at(const coord_def &c, int colour); bool dgn_place_map(const map_def *map, bool clobber, bool make_no_exits, const coord_def &pos = coord_def(-1, -1)); + void level_clear_vault_memory(); void level_welcome_messages(); @@ -256,6 +258,16 @@ bool octa_room(spec_room &sr, int oblique_max, int count_feature_in_box(int x0, int y0, int x1, int y1, dungeon_feature_type feat); + +void dgn_replace_area(const coord_def& p1, const coord_def& p2, + dungeon_feature_type replace, + dungeon_feature_type feature, + unsigned mmask = 0, bool needs_update = false); +void dgn_replace_area(int sx, int sy, int ex, int ey, + dungeon_feature_type replace, + dungeon_feature_type feature, + unsigned mmask = 0, bool needs_update = false); + inline int count_feature_in_box( const coord_def& p1, const coord_def& p2, dungeon_feature_type feat ) { diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc index 30687dd58d..bede6d46c2 100644 --- a/crawl-ref/source/effects.cc +++ b/crawl-ref/source/effects.cc @@ -27,6 +27,7 @@ #include "coordit.h" #include "decks.h" #include "delay.h" +#include "dgn-shoals.h" #include "dungeon.h" #include "directn.h" #include "dgnevent.h" @@ -4263,6 +4264,7 @@ void update_level(double elapsedTime) #endif update_corpses(elapsedTime); + shoals_apply_tides(turns); if (env.sanctuary_time) { diff --git a/crawl-ref/source/env.h b/crawl-ref/source/env.h index 1f3c02f142..aa78f2ac0f 100644 --- a/crawl-ref/source/env.h +++ b/crawl-ref/source/env.h @@ -6,6 +6,7 @@ #include "show.h" #include "trap_def.h" +typedef FixedArray<short, GXM, GYM> grid_heightmap; struct crawl_environment { public: @@ -22,6 +23,8 @@ public: FixedArray< unsigned short, GXM, GYM > cgrid; // cloud grid FixedArray< unsigned short, GXM, GYM > grid_colours; // colour overrides + std::auto_ptr<grid_heightmap> heightmap; + // Player-remembered terrain. TODO: move to class player. FixedArray< map_cell, GXM, GYM > map_knowledge; diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index e1a8083ef3..5f4f5e7e40 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -257,9 +257,13 @@ struct cloud_struct unsigned char spread_rate; kill_category whose; killer_type killer; + int colour; + std::string name; + std::string tile; cloud_struct() : pos(), type(CLOUD_NONE), decay(0), spread_rate(0), - whose(KC_OTHER), killer(KILL_NONE) + whose(KC_OTHER), killer(KILL_NONE), colour(-1), + name(""), tile("") { } @@ -529,6 +533,12 @@ public: bool is_valid() const; + // Returns true if this item should be preserved as far as possible. + bool is_critical() const; + + // Returns true if this item should not normally be enchanted. + bool is_mundane() const; + private: std::string name_aux(description_level_type desc, bool terse, bool ident, diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index 1543ddabb1..b2237a92cb 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -374,7 +374,7 @@ void melee_attack::check_hand_half_bonus_eligible() && !can_do_unarmed && !shield && weapon - && !item_cursed(*weapon) + && !weapon->cursed() && hands == HANDS_HALF); } @@ -3832,7 +3832,7 @@ int melee_attack::player_to_hit(bool random_factor) && !can_do_unarmed && !shield && weapon - && !item_cursed( *weapon ) + && !weapon ->cursed() && hands == HANDS_HALF; int your_to_hit = 15 + (calc_stat_to_hit_base() / 2); diff --git a/crawl-ref/source/food.cc b/crawl-ref/source/food.cc index 112f9e1739..bd2db8d728 100644 --- a/crawl-ref/source/food.cc +++ b/crawl-ref/source/food.cc @@ -377,7 +377,7 @@ static void _terminate_butchery(bool wpn_switch, bool removed_gloves, { // Switch weapon back. if (wpn_switch && you.equip[EQ_WEAPON] != old_weapon - && (!you.weapon() || !item_cursed(*you.weapon()))) + && (!you.weapon() || !you.weapon()->cursed())) { start_delay(DELAY_WEAPON_SWAP, 1, old_weapon); } @@ -479,7 +479,7 @@ bool butchery(int which_corpse) && !player_wearing_slot(EQ_GLOVES); bool gloved_butcher = (you.has_claws() && player_wearing_slot(EQ_GLOVES) - && !item_cursed(you.inv[you.equip[EQ_GLOVES]])); + && !you.inv[you.equip[EQ_GLOVES]].cursed()); bool can_butcher = (teeth_butcher || barehand_butcher || you.weapon() && can_cut_meat(*you.weapon())); diff --git a/crawl-ref/source/godwrath.cc b/crawl-ref/source/godwrath.cc index a94b0ec2c8..e3e87198ca 100644 --- a/crawl-ref/source/godwrath.cc +++ b/crawl-ref/source/godwrath.cc @@ -1249,7 +1249,8 @@ static void _tso_blasts_cleansing_flame(const char *message) simple_god_message(" blasts you with cleansing flame!", GOD_SHINING_ONE); - cleansing_flame(20 + (you.experience_level * 7) / 3, + // damage is 2d(pow), *3/2 for undead and demonspawn + cleansing_flame(5 + (you.experience_level * 7) / 12, CLEANSING_FLAME_TSO, you.pos()); } } diff --git a/crawl-ref/source/invent.cc b/crawl-ref/source/invent.cc index 047a502467..887fbd436c 100644 --- a/crawl-ref/source/invent.cc +++ b/crawl-ref/source/invent.cc @@ -109,7 +109,7 @@ const std::string &InvEntry::get_fullname() const bool InvEntry::is_item_cursed() const { - return (item_ident(*item, ISFLAG_KNOW_CURSE) && item_cursed(*item)); + return (item_ident(*item, ISFLAG_KNOW_CURSE) && item->cursed()); } bool InvEntry::is_item_glowing() const diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc index 38bf295a7a..fd01008e25 100644 --- a/crawl-ref/source/item_use.cc +++ b/crawl-ref/source/item_use.cc @@ -112,7 +112,7 @@ bool can_wield(item_def *weapon, bool say_reason, if (!ignore_temporary_disability && you.weapon() && you.weapon()->base_type == OBJ_WEAPONS - && item_cursed(*you.weapon())) + && you.weapon()->cursed()) { SAY(mpr("You can't unwield your weapon to draw a new one!")); return (false); @@ -721,7 +721,7 @@ void wield_effects(int item_wield_2, bool showMsgs) } } - if (item_cursed(item)) + if (item.cursed()) { mpr("It sticks to your hand!"); int amusement = 16; @@ -1059,7 +1059,7 @@ bool do_wear_armour(int item, bool quiet) && you.equip[EQ_CLOAK] != -1 && !cloak_is_being_removed()) { if (you.equip[EQ_BODY_ARMOUR] != -1 - && item_cursed(you.inv[you.equip[EQ_BODY_ARMOUR]])) + && you.inv[you.equip[EQ_BODY_ARMOUR]].cursed()) { if (!quiet) { @@ -1069,7 +1069,7 @@ bool do_wear_armour(int item, bool quiet) } return (false); } - if (!item_cursed(you.inv[you.equip[EQ_CLOAK]])) + if (!you.inv[you.equip[EQ_CLOAK]].cursed()) { cloak = you.equip[EQ_CLOAK]; if (!takeoff_armour(you.equip[EQ_CLOAK])) @@ -1149,7 +1149,7 @@ bool takeoff_armour(int item) } // If we get here, we're wearing the item. - if (item_cursed(invitem)) + if (invitem.cursed()) { mprf("%s is stuck to your body!", invitem.name(DESC_CAP_YOUR).c_str()); return (false); @@ -1165,7 +1165,7 @@ bool takeoff_armour(int item) { if (you.equip[EQ_CLOAK] != -1 && !cloak_is_being_removed()) { - if (!item_cursed(you.inv[you.equip[EQ_CLOAK]])) + if (!you.inv[you.equip[EQ_CLOAK]].cursed()) { cloak = you.equip[ EQ_CLOAK ]; if (!takeoff_armour(you.equip[EQ_CLOAK])) @@ -1453,7 +1453,7 @@ static bool _fire_validate_item(int slot, std::string &err) { if (slot == you.equip[EQ_WEAPON] && you.inv[slot].base_type == OBJ_WEAPONS - && item_cursed(you.inv[slot])) + && you.inv[slot].cursed()) { err = "That weapon is stuck to your hand!"; return (false); @@ -3481,7 +3481,7 @@ void jewellery_wear_effects(item_def &item) set_ident_flags(item, ISFLAG_EQ_JEWELLERY_MASK); } - if (item_cursed(item)) + if (item.cursed()) { mprf("Oops, that %s feels deathly cold.", jewellery_is_amulet(item)? "amulet" : "ring"); @@ -3510,7 +3510,7 @@ static int _prompt_ring_to_remove(int new_ring) const item_def *left = you.slot_item(EQ_LEFT_RING); const item_def *right = you.slot_item(EQ_RIGHT_RING); - if (item_cursed(*left) && item_cursed(*right)) + if (left->cursed() && right->cursed()) { mprf("You're already wearing two cursed rings!"); return (-1); @@ -3730,7 +3730,7 @@ static bool _swap_rings(int ring_slot) const item_def* lring = you.slot_item(EQ_LEFT_RING); const item_def* rring = you.slot_item(EQ_RIGHT_RING); - if (item_cursed(*lring) && item_cursed(*rring)) + if (lring->cursed() && rring->cursed()) { mprf("You're already wearing two cursed rings!"); return (false); @@ -3744,7 +3744,7 @@ static bool _swap_rings(int ring_slot) && lring->plus2 == rring->plus2 && !is_artefact(*lring) && !is_artefact(*rring)) { - if (item_cursed(*lring)) + if (lring->cursed()) unwanted = you.equip[EQ_RIGHT_RING]; else unwanted = you.equip[EQ_LEFT_RING]; @@ -3811,7 +3811,7 @@ bool puton_item(int item_slot) if (!is_amulet) // i.e. it's a ring { const item_def* gloves = you.slot_item(EQ_GLOVES); - if (gloves && item_cursed(*gloves)) + if (gloves && gloves->cursed()) { mpr("You can't take your gloves off to put on a ring!"); return (false); @@ -4025,7 +4025,7 @@ bool remove_ring(int slot, bool announce) } if (you.equip[EQ_GLOVES] != -1 - && item_cursed( you.inv[you.equip[EQ_GLOVES]] ) + && you.inv[you.equip[EQ_GLOVES]] .cursed() && you.equip[EQ_AMULET] == -1) { mpr("You can't take your gloves off to remove any rings!"); @@ -4089,7 +4089,7 @@ bool remove_ring(int slot, bool announce) } if (you.equip[EQ_GLOVES] != -1 - && item_cursed( you.inv[you.equip[EQ_GLOVES]] ) + && you.inv[you.equip[EQ_GLOVES]] .cursed() && (hand_used == EQ_LEFT_RING || hand_used == EQ_RIGHT_RING)) { mpr("You can't take your gloves off to remove any rings!"); @@ -4102,7 +4102,7 @@ bool remove_ring(int slot, bool announce) return (false); } - if (item_cursed( you.inv[you.equip[hand_used]] )) + if (you.inv[you.equip[hand_used]] .cursed()) { if (announce) { @@ -4771,7 +4771,7 @@ bool enchant_weapon(enchant_stat_type which_stat, bool quiet, item_def &wpn) return (false); } - const bool is_cursed = item_cursed(wpn); + const bool is_cursed = wpn.cursed(); // Missiles only have one stat. if (wpn.base_type == OBJ_MISSILES) @@ -4882,7 +4882,7 @@ bool enchant_armour(int &ac_change, bool quiet, item_def &arm) return (false); } - const bool is_cursed = item_cursed(arm); + const bool is_cursed = arm.cursed(); // Turn hides into mails where applicable. // NOTE: It is assumed that armour which changes in this way does @@ -5355,7 +5355,7 @@ void read_scroll(int slot) case SCR_CURSE_WEAPON: if (!you.weapon() || you.weapon()->base_type != OBJ_WEAPONS - || item_cursed(*you.weapon())) + || you.weapon()->cursed()) { canned_msg(MSG_NOTHING_HAPPENS); id_the_scroll = false; @@ -5383,7 +5383,7 @@ void read_scroll(int slot) { item_def& wpn = *you.weapon(); - const bool is_cursed = item_cursed(wpn); + const bool is_cursed = wpn.cursed(); if (wpn.base_type != OBJ_WEAPONS && wpn.base_type != OBJ_MISSILES || !is_cursed @@ -5491,7 +5491,7 @@ void read_scroll(int slot) int affected = EQ_WEAPON; for (int i = EQ_CLOAK; i <= EQ_BODY_ARMOUR; i++) { - if (you.equip[i] != -1 && !item_cursed(you.inv[you.equip[i]])) + if (you.equip[i] != -1 && !you.inv[you.equip[i]].cursed()) { count++; if (one_chance_in(count)) @@ -5695,7 +5695,7 @@ void use_artefact(item_def &item, bool *show_msgs, bool unmeld) artefact_wpn_learn_prop(item, ARTP_BERSERK); } - if (!unmeld && !item_cursed(item) && proprt[ARTP_CURSED] > 0 + if (!unmeld && !item.cursed() && proprt[ARTP_CURSED] > 0 && one_chance_in(proprt[ARTP_CURSED])) { do_curse_item( item, false ); diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc index 8d487d2682..c8ce048107 100644 --- a/crawl-ref/source/itemname.cc +++ b/crawl-ref/source/itemname.cc @@ -1037,7 +1037,7 @@ std::string item_def::name_aux(description_level_type desc, // this behaviour, *please* make it so that there is an option // that maintains this behaviour. -- bwr // Nor for artefacts. Again, the state should be obvious. --jpeg - if (item_cursed(*this)) + if (cursed()) buff << "cursed "; else if (Options.show_uncursed && !know_pluses && (!know_type || !is_artefact(*this))) @@ -1108,7 +1108,7 @@ std::string item_def::name_aux(description_level_type desc, if (know_brand) buff << weapon_brand_name(*this, terse); - if (know_curse && item_cursed(*this) && terse) + if (know_curse && cursed() && terse) buff << " (curse)"; break; @@ -1197,7 +1197,7 @@ std::string item_def::name_aux(description_level_type desc, case OBJ_ARMOUR: if (know_curse && !terse) { - if (item_cursed(*this)) + if (cursed()) buff << "cursed "; else if (Options.show_uncursed && !know_pluses) buff << "uncursed "; @@ -1310,7 +1310,7 @@ std::string item_def::name_aux(description_level_type desc, } } - if (know_curse && item_cursed(*this) && terse) + if (know_curse && cursed() && terse) buff << " (curse)"; break; @@ -1462,7 +1462,7 @@ std::string item_def::name_aux(description_level_type desc, if (know_curse) { - if (item_cursed(*this)) + if (cursed()) buff << "cursed "; else if (Options.show_uncursed && !terse && (!is_randart || !know_type) diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc index 720dfff844..bf47f3ab96 100644 --- a/crawl-ref/source/itemprop.cc +++ b/crawl-ref/source/itemprop.cc @@ -457,11 +457,6 @@ void init_properties() // // Item cursed status functions: // -bool item_cursed( const item_def &item ) -{ - return (item.flags & ISFLAG_CURSED); -} - bool item_known_cursed( const item_def &item ) { return ((item.flags & ISFLAG_KNOW_CURSE) && (item.flags & ISFLAG_CURSED)); @@ -567,48 +562,6 @@ bool item_ident( const item_def &item, unsigned long flags ) return ((item.flags & flags) == flags); } -// The Orb of Zot and unique runes are considered critical. -bool item_is_critical(const item_def &item) -{ - if (!item.is_valid()) - return (false); - - if (item.base_type == OBJ_ORBS) - return (true); - - return (item.base_type == OBJ_MISCELLANY - && item.sub_type == MISC_RUNE_OF_ZOT - && item.plus != RUNE_DEMONIC - && item.plus != RUNE_ABYSSAL); -} - -// Is item something that no one would usually bother enchanting? -bool item_is_mundane(const item_def &item) -{ - switch (item.base_type) - { - case OBJ_WEAPONS: - if (item.sub_type == WPN_CLUB - || item.sub_type == WPN_GIANT_CLUB - || item.sub_type == WPN_GIANT_SPIKED_CLUB - || item.sub_type == WPN_KNIFE) - { - return (true); - } - break; - - case OBJ_ARMOUR: - if (item.sub_type == ARM_ANIMAL_SKIN) - return (true); - break; - - default: - break; - } - - return (false); -} - void set_ident_flags( item_def &item, unsigned long flags ) { preserve_quiver_slots p; @@ -1210,7 +1163,7 @@ bool is_enchantable_weapon(const item_def &wpn, bool uncurse, bool first) || first && wpn.plus >= MAX_WPN_ENCHANT || !first && wpn.plus2 >= MAX_WPN_ENCHANT) { - return (uncurse && item_cursed(wpn)); + return (uncurse && wpn.cursed()); } } // Highly enchanted missiles, which have only one stat, cannot be @@ -1243,7 +1196,7 @@ bool is_enchantable_armour(const item_def &arm, bool uncurse, bool unknown) // Artefacts or highly enchanted armour cannot be enchanted, only // uncursed. if (is_artefact(arm) || arm.plus >= armour_max_enchant(arm)) - return (uncurse && item_cursed(arm)); + return (uncurse && arm.cursed()); return (true); } diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h index a8f1eceabd..5604d352f5 100644 --- a/crawl-ref/source/itemprop.h +++ b/crawl-ref/source/itemprop.h @@ -14,14 +14,7 @@ struct bolt; void init_properties(void); -// Returns true if this item should be preserved as far as possible. -bool item_is_critical(const item_def &item); - -// Returns true if this item should not normally be enchanted. -bool item_is_mundane(const item_def &item); - // cursed: -bool item_cursed( const item_def &item ); bool item_known_cursed( const item_def &item ); bool item_known_uncursed( const item_def &item ); void do_curse_item( item_def &item, bool quiet = true ); diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc index a3418761d4..29b25a021b 100644 --- a/crawl-ref/source/items.cc +++ b/crawl-ref/source/items.cc @@ -1921,7 +1921,7 @@ bool drop_item( int item_dropped, int quant_drop, bool try_offer ) if (item_dropped == you.equip[EQ_WEAPON] && you.inv[item_dropped].base_type == OBJ_WEAPONS - && item_cursed( you.inv[item_dropped] )) + && you.inv[item_dropped] .cursed()) { mpr("That object is stuck to you!"); return (false); @@ -2821,7 +2821,7 @@ int item_def::book_number() const bool item_def::cursed() const { - return (item_cursed(*this)); + return (flags & ISFLAG_CURSED); } bool item_def::launched_by(const item_def &launcher) const @@ -2919,6 +2919,48 @@ bool item_def::is_valid() const return (base_type != OBJ_UNASSIGNED && quantity > 0); } +// The Orb of Zot and unique runes are considered critical. +bool item_def::is_critical() const +{ + if (!is_valid()) + return (false); + + if (base_type == OBJ_ORBS) + return (true); + + return (base_type == OBJ_MISCELLANY + && sub_type == MISC_RUNE_OF_ZOT + && plus != RUNE_DEMONIC + && plus != RUNE_ABYSSAL); +} + +// Is item something that no one would usually bother enchanting? +bool item_def::is_mundane() const +{ + switch (base_type) + { + case OBJ_WEAPONS: + if (sub_type == WPN_CLUB + || sub_type == WPN_GIANT_CLUB + || sub_type == WPN_GIANT_SPIKED_CLUB + || sub_type == WPN_KNIFE) + { + return (true); + } + break; + + case OBJ_ARMOUR: + if (sub_type == ARM_ANIMAL_SKIN) + return (true); + break; + + default: + break; + } + + return (false); +} + static void _rune_from_specs(const char* _specs, item_def &item) { char specs[80]; diff --git a/crawl-ref/source/l_dgn.cc b/crawl-ref/source/l_dgn.cc index 99035d37f0..dddb216da4 100644 --- a/crawl-ref/source/l_dgn.cc +++ b/crawl-ref/source/l_dgn.cc @@ -1173,14 +1173,16 @@ static int lua_cloud_pow_rolls; static int make_a_lua_cloud(coord_def where, int garbage, int spread_rate, cloud_type ctype, kill_category whose, - killer_type killer) + killer_type killer, int colour, std::string name, + std::string tile) { UNUSED( garbage ); const int pow = random_range(lua_cloud_pow_min, lua_cloud_pow_max, lua_cloud_pow_rolls); - place_cloud( ctype, where, pow, whose, killer, spread_rate ); + place_cloud( ctype, where, pow, whose, killer, spread_rate, colour, name, + tile ); return 1; } @@ -1200,6 +1202,10 @@ static int dgn_apply_area_cloud(lua_State *ls) const int spread_rate = lua_isnumber(ls, 9) ? luaL_checkint(ls, 9) : -1; + const int colour = lua_isstring(ls, 10) ? str_to_colour(luaL_checkstring(ls, 10)) : -1; + std::string name = lua_isstring(ls, 11) ? luaL_checkstring(ls, 11) : ""; + std::string tile = lua_isstring(ls, 12) ? luaL_checkstring(ls, 12) : ""; + if (!in_bounds(x, y)) { char buf[80]; @@ -1265,7 +1271,7 @@ static int dgn_apply_area_cloud(lua_State *ls) apply_area_cloud(make_a_lua_cloud, coord_def(x, y), 0, size, ctype, kc, cloud_struct::whose_to_killer(kc), - spread_rate); + spread_rate, colour, name, tile); return (0); } @@ -1282,6 +1288,10 @@ static int dgn_place_cloud(lua_State *ls) const int spread_rate = lua_isnumber(ls, 6) ? luaL_checkint(ls, 6) : -1; + const int colour = lua_isstring(ls, 7) ? str_to_colour(luaL_checkstring(ls, 7)) : -1; + std::string name = lua_isstring(ls, 8) ? luaL_checkstring(ls, 8) : ""; + std::string tile = lua_isstring(ls, 9) ? luaL_checkstring(ls, 9) : ""; + if (!in_bounds(x, y)) { char buf[80]; @@ -1315,7 +1325,7 @@ static int dgn_place_cloud(lua_State *ls) return (0); } - place_cloud(ctype, coord_def(x, y), cl_range, kc, spread_rate); + place_cloud(ctype, coord_def(x, y), cl_range, kc, spread_rate, colour, name, tile); return (0); } diff --git a/crawl-ref/source/l_item.cc b/crawl-ref/source/l_item.cc index c2248e1682..80bbc88df9 100644 --- a/crawl-ref/source/l_item.cc +++ b/crawl-ref/source/l_item.cc @@ -463,7 +463,7 @@ static int l_item_cursed(lua_State *ls) { LUA_ITEM(item, 1); bool cursed = item && item_ident(*item, ISFLAG_KNOW_CURSE) - && item_cursed(*item); + && item->cursed(); lua_pushboolean(ls, cursed); return (1); } diff --git a/crawl-ref/source/main.cc b/crawl-ref/source/main.cc index c1b4c6d615..9dcd774a00 100644 --- a/crawl-ref/source/main.cc +++ b/crawl-ref/source/main.cc @@ -533,8 +533,8 @@ static void _do_wizard_command(int wiz_command, bool silent_fail) case 'I': wizard_unidentify_pack(); break; case 'Z': case 'z': wizard_cast_spec_spell(); break; - case '(': wizard_create_feature_number(); break; - case ')': wizard_create_feature_name(); break; + case '(': + case ')': wizard_create_feature(); break; case ':': wizard_list_branches(); break; case '{': wizard_map_level(); break; case '}': wizard_reveal_traps(); break; diff --git a/crawl-ref/source/makefile.obj b/crawl-ref/source/makefile.obj index 8761eb11e7..1b1d038f31 100644 --- a/crawl-ref/source/makefile.obj +++ b/crawl-ref/source/makefile.obj @@ -34,6 +34,7 @@ dgnevent.o \ directn.o \ dlua.o \ dungeon.o \ +dgn-shoals.o \ effects.o \ exclude.o \ feature.o \ @@ -101,6 +102,7 @@ menu.o \ message.o \ mgen_data.o \ misc.o \ +mislead.o \ mon-abil.o \ mon-act.o \ mon-behv.o \ diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc index ee473f1a04..442b500112 100644 --- a/crawl-ref/source/makeitem.cc +++ b/crawl-ref/source/makeitem.cc @@ -1699,7 +1699,7 @@ brand_ok: } else if ((force_good || is_demonic(item) || forced_ego || x_chance_in_y(51 + item_level, 200)) - && (!item_is_mundane(item) || force_good)) + && (!item.is_mundane() || force_good)) { // Make a better item (possibly ego). if (!no_brand) @@ -2399,7 +2399,7 @@ static void _generate_armour_item(item_def& item, bool allow_uniques, } else if ((force_good || forced_ego || item.sub_type == ARM_WIZARD_HAT || x_chance_in_y(51 + item_level, 250)) - && (!item_is_mundane(item) || force_good)) + && (!item.is_mundane() || force_good)) { // Make a good item... item.plus += random2(3); @@ -2924,7 +2924,7 @@ static void _generate_jewellery_item(item_def& item, bool allow_uniques, if (item.sub_type == RING_SLAYING ) // requires plus2 too { - if (item_cursed(item) && !one_chance_in(20)) + if (item.cursed() && !one_chance_in(20)) item.plus2 = -1 - random2avg(6, 2); else { @@ -3233,7 +3233,7 @@ static bool _weapon_is_visibly_special(const item_def &item) if (visibly_branded || is_artefact(item)) return (true); - if (item_is_mundane(item)) + if (item.is_mundane()) return (false); if ((item.plus || item.plus2) @@ -3256,7 +3256,7 @@ static bool _armour_is_visibly_special(const item_def &item) if (visibly_branded || is_artefact(item)) return (true); - if (item_is_mundane(item)) + if (item.is_mundane()) return (false); if (item.plus && !one_chance_in(3)) diff --git a/crawl-ref/source/mapdef.cc b/crawl-ref/source/mapdef.cc index 060ec78677..5eda9e7792 100644 --- a/crawl-ref/source/mapdef.cc +++ b/crawl-ref/source/mapdef.cc @@ -3898,20 +3898,17 @@ item_spec item_list::parse_single_spec(std::string s) } } - if (s.find("damaged ") == 0) + if (strip_tag(s, "damaged")) { result.level = ISPEC_DAMAGED; - s = s.substr(8); } - if (s.find("cursed ") == 0) + if (strip_tag(s, "cursed")) { result.level = ISPEC_BAD; // damaged + cursed, actually - s = s.substr(7); } - if (s.find("randart ") == 0) + if (strip_tag(s, "randart")) { result.level = ISPEC_RANDART; - s = s.substr(8); } if (strip_tag(s, "no_uniq")) diff --git a/crawl-ref/source/maps.cc b/crawl-ref/source/maps.cc index 07da121e02..072b7c8557 100644 --- a/crawl-ref/source/maps.cc +++ b/crawl-ref/source/maps.cc @@ -337,7 +337,7 @@ static bool _safe_vault_place(const map_def &map, if (lines[dp.y][dp.x] == ' ') continue; - if (dgn_Map_Mask[cp.x][cp.y] == MMT_VAULT) + if (dgn_Map_Mask[cp.x][cp.y] & MMT_VAULT) return (false); const dungeon_feature_type dfeat = grd(cp); diff --git a/crawl-ref/source/message.cc b/crawl-ref/source/message.cc index 6a3d36c487..a6adba6847 100644 --- a/crawl-ref/source/message.cc +++ b/crawl-ref/source/message.cc @@ -413,12 +413,17 @@ int channel_to_colour( msg_channel_type channel, int param ) static void do_message_print( msg_channel_type channel, int param, const char *format, va_list argp ) { - // Is this limit intentional? char buff[200]; - vsnprintf( buff, sizeof( buff ), format, argp ); - buff[199] = 0; - - mpr(buff, channel, param); + size_t len = vsnprintf( buff, sizeof( buff ), format, argp ); + if (len < sizeof( buff )) { + mpr( buff, channel, param ); + } + else { + char *heapbuf = (char*)malloc( len + 1 ); + vsnprintf( heapbuf, len + 1, format, argp ); + mpr( heapbuf, channel, param ); + free( heapbuf ); + } } void mprf( msg_channel_type channel, int param, const char *format, ... ) diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 5697133c4a..b5c9e6bb40 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -36,6 +36,7 @@ #include "coordit.h" #include "database.h" #include "delay.h" +#include "dgn-shoals.h" #include "directn.h" #include "dgnevent.h" #include "directn.h" @@ -2888,14 +2889,15 @@ bool i_feel_safe(bool announce, bool want_move, bool just_monsters, int range) // check clouds if (in_bounds(you.pos()) && env.cgrid(you.pos()) != EMPTY_CLOUD) { - const cloud_type type = env.cloud[env.cgrid(you.pos())].type; + const int cloudidx = env.cgrid(you.pos()); + const cloud_type type = env.cloud[cloudidx].type; if (is_damaging_cloud(type, want_move)) { if (announce) { mprf(MSGCH_WARN, "You're standing in a cloud of %s!", - cloud_name(type).c_str()); + cloud_name(cloudidx).c_str()); } return (false); } @@ -3092,6 +3094,7 @@ void run_environment_effects() } run_corruption_effects(you.time_taken); + shoals_apply_tides(div_rand_round(you.time_taken, 10)); } coord_def pick_adjacent_free_square(const coord_def& p) diff --git a/crawl-ref/source/mislead.cc b/crawl-ref/source/mislead.cc new file mode 100644 index 0000000000..64c574d97a --- /dev/null +++ b/crawl-ref/source/mislead.cc @@ -0,0 +1,127 @@ +/* File: mislead.cc + * Summary: Handling of the Mislead spell and stats + */ + +#include "AppHdr.h" +#include "mislead.h" + +#include "enum.h" +#include "env.h" +#include "message.h" +#include "monster.h" +#include "mon-iter.h" +#include "mon-util.h" +#include "view.h" +#include "random.h" +#include "tutorial.h" +#include "xom.h" + +bool unsuitable_misled_monster(monster_type mons) +{ + return (mons_is_unique(mons) || mons_is_mimic(mons) + || mons_class_is_stationary(mons) || mons_genus(mons) == MONS_DRACONIAN + || mons == MONS_DANCING_WEAPON || mons == MONS_UGLY_THING + || mons == MONS_VERY_UGLY_THING || mons == MONS_ZOMBIE_SMALL + || mons == MONS_ZOMBIE_LARGE || mons == MONS_SKELETON_SMALL + || mons == MONS_SKELETON_LARGE || mons == MONS_SIMULACRUM_SMALL + || mons == MONS_SIMULACRUM_LARGE || mons == MONS_SPECTRAL_THING + || mons == MONS_SLIME_CREATURE || mons == MONS_BALLISTOMYCETE + || mons == MONS_HYDRA || mons == MONS_PLAYER_GHOST + || mons == MONS_SHAPESHIFTER || mons == MONS_PANDEMONIUM_DEMON + || mons == MONS_KILLER_KLOWN || mons == MONS_KRAKEN + || mons == MONS_KRAKEN_TENTACLE + || mons == MONS_GLOWING_SHAPESHIFTER + || mons == MONS_GIANT_BAT); +} + +monster_type get_misled_monster (monsters *monster) +{ + monster_type mons = random_monster_at_grid(monster->pos()); + if (unsuitable_misled_monster(mons)) + mons = random_monster_at_grid(monster->pos()); + + if (unsuitable_misled_monster(mons)) + return (MONS_GIANT_BAT); + + return mons; +} + +bool update_mislead_monster(monsters* monster) +{ + // Don't affect uniques, named monsters, and monsters with special tiles. + if (mons_is_unique(monster->type) || !monster->mname.empty() + || monster->props.exists("monster_tile") + || monster->props.exists("mislead_as")) + { + return (false); + } + + short misled_as = get_misled_monster(monster); + monster->props["mislead_as"] = misled_as; + + if (misled_as == MONS_GIANT_BAT) + return (false); + + return (true); +} + +int update_mislead_monsters(monsters* caster) +{ + int count = 0; + + for (monster_iterator mi; mi; ++mi) + if (*mi != caster && update_mislead_monster(*mi)) + count++; + + return count; +} + +void mons_cast_mislead(monsters *monster) +{ + // This really only affects the player; it causes confusion when cast on + // non-player foes, but that is dealt with inside ye-great-Switch-of-Doom. + if (monster->foe != MHITYOU) + return; + + // Prevents Mislead spam by Mara and co. {due} + if (you.duration[DUR_MISLED] > 10 && one_chance_in(3)) + return; + + if (wearing_amulet(AMU_CLARITY)) + { + mpr("Your vision blurs momentarily."); + return; + } + + update_mislead_monsters(monster); + + const int old_value = you.duration[DUR_MISLED]; + you.increase_duration(DUR_MISLED, monster->hit_dice * 12 / 3, 50); + if (you.duration[DUR_MISLED] > old_value) + { + you.check_awaken(500); + + if (old_value <= 0) + { + mpr("But for a moment, strange images dance in front of your eyes.", MSGCH_WARN); +#ifdef USE_TILE + tiles.add_overlay(you.pos(), tileidx_zap(MAGENTA)); + update_screen(); +#else + flash_view(MAGENTA); +#endif + more(); + } + else + mpr("You are even more misled!", MSGCH_WARN); + + learned_something_new(TUT_YOU_ENCHANTED); + + xom_is_stimulated((you.duration[DUR_MISLED] - old_value) + / BASELINE_DELAY); + } + + return; +} + + diff --git a/crawl-ref/source/mislead.h b/crawl-ref/source/mislead.h new file mode 100644 index 0000000000..540ffea9b1 --- /dev/null +++ b/crawl-ref/source/mislead.h @@ -0,0 +1,8 @@ +/* File: mislead.h + * Summary: Handling of the Mislead spell and stats + */ + +bool unsuitable_misled_monster(monster_type mons); +monster_type get_misled_monster (monsters *monster); +int update_mislead_monsters(monsters* caster); +bool update_mislead_monster(monsters* monster); diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc index 761521f7ab..7bbbd77862 100644 --- a/crawl-ref/source/mon-cast.cc +++ b/crawl-ref/source/mon-cast.cc @@ -24,6 +24,7 @@ #include "mon-place.h" #include "terrain.h" #include "tutorial.h" +#include "mislead.h" #include "mgen_data.h" #include "coord.h" #include "mon-speak.h" @@ -1564,108 +1565,6 @@ int _count_mara_fakes() return count; } -bool _unsuitable_misled_monster(monster_type mons) -{ - return (mons_is_unique(mons) || mons_is_mimic(mons) - || mons_class_is_stationary(mons) || mons_genus(mons) == MONS_DRACONIAN - || mons == MONS_DANCING_WEAPON || mons == MONS_UGLY_THING - || mons == MONS_VERY_UGLY_THING || mons == MONS_ZOMBIE_SMALL - || mons == MONS_ZOMBIE_LARGE || mons == MONS_SKELETON_SMALL - || mons == MONS_SKELETON_LARGE || mons == MONS_SIMULACRUM_SMALL - || mons == MONS_SIMULACRUM_LARGE || mons == MONS_SPECTRAL_THING - || mons == MONS_SLIME_CREATURE || mons == MONS_BALLISTOMYCETE - || mons == MONS_HYDRA || mons == MONS_PLAYER_GHOST - || mons == MONS_SHAPESHIFTER || mons == MONS_PANDEMONIUM_DEMON - || mons == MONS_KILLER_KLOWN || mons == MONS_KRAKEN - || mons == MONS_KRAKEN_TENTACLE - || mons == MONS_GLOWING_SHAPESHIFTER); -} - -monster_type _get_misled_monster (monsters *monster) -{ - monster_type mons = random_monster_at_grid(monster->pos()); - if (_unsuitable_misled_monster(mons)) - mons = random_monster_at_grid(monster->pos()); - - if (_unsuitable_misled_monster(mons)) - return (MONS_GIANT_BAT); - - return mons; -} - -int _update_mislead_monsters(monsters* monster) -{ - int count = 0; - - for (monster_iterator mi; mi; ++mi) - { - if (*mi == monster) - continue; - - // Don't affect uniques, named monsters, and monsters with special tiles. - if (mons_is_unique(mi->type) || !mi->mname.empty() - || mi->props.exists("monster_tile") || mi->props.exists("mislead_as")) - { - continue; - } - else - { - mi->props["mislead_as"] = short(_get_misled_monster(*mi)); - count++; - } - } - - return count; -} - -void mons_cast_mislead(monsters *monster) -{ - // This really only affects the player; it causes confusion when cast on - // non-player foes, but that is dealt with inside ye-great-Switch-of-Doom. - if (monster->foe != MHITYOU) - return; - - // Prevents Mislead spam by Mara and co. {due} - if (you.duration[DUR_MISLED] > 10 && one_chance_in(3)) - return; - - if (wearing_amulet(AMU_CLARITY)) - { - mpr("Your vision blurs momentarily."); - return; - } - - _update_mislead_monsters(monster); - - const int old_value = you.duration[DUR_MISLED]; - you.increase_duration(DUR_MISLED, monster->hit_dice * 12 / 3, 50); - if (you.duration[DUR_MISLED] > old_value) - { - you.check_awaken(500); - - if (old_value <= 0) - { - mpr("But for a moment, strange images dance in front of your eyes.", MSGCH_WARN); -#ifdef USE_TILE - tiles.add_overlay(you.pos(), tileidx_zap(MAGENTA)); - update_screen(); -#else - flash_view(MAGENTA); -#endif - more(); - } - else - mpr("You are even more misled!", MSGCH_WARN); - - learned_something_new(TUT_YOU_ENCHANTED); - - xom_is_stimulated((you.duration[DUR_MISLED] - old_value) - / BASELINE_DELAY); - } - - return; -} - bool _find_players_ghost () { bool found = false; diff --git a/crawl-ref/source/mon-place.cc b/crawl-ref/source/mon-place.cc index 9faa371cb8..3a88d27774 100644 --- a/crawl-ref/source/mon-place.cc +++ b/crawl-ref/source/mon-place.cc @@ -24,6 +24,7 @@ #include "lev-pand.h" #include "makeitem.h" #include "message.h" +#include "mislead.h" #include "mon-behv.h" #include "mon-gear.h" #include "mon-iter.h" @@ -1242,6 +1243,9 @@ static int _place_monster_aux(const mgen_data &mg, mon->add_ench(ENCH_SLOWLY_DYING); } + if (!crawl_state.arena && you.misled()) + update_mislead_monster(mon); + if (monster_can_submerge(mon, grd(fpos)) && !one_chance_in(5)) mon->add_ench(ENCH_SUBMERGED); diff --git a/crawl-ref/source/mon-stuff.cc b/crawl-ref/source/mon-stuff.cc index d8f0a8e7a2..730fb54ef2 100644 --- a/crawl-ref/source/mon-stuff.cc +++ b/crawl-ref/source/mon-stuff.cc @@ -198,7 +198,7 @@ bool curse_an_item( bool decay_potions, bool quiet ) || you.inv[i].base_type == OBJ_JEWELLERY || you.inv[i].base_type == OBJ_POTIONS) { - if (item_cursed( you.inv[i] )) + if (you.inv[i] .cursed()) continue; if (you.inv[i].base_type != OBJ_POTIONS diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc index 4cefe30857..5285da7795 100644 --- a/crawl-ref/source/monster.cc +++ b/crawl-ref/source/monster.cc @@ -577,11 +577,11 @@ bool monsters::can_wield(const item_def& item, bool ignore_curse, if (!ignore_curse) { int num_cursed = 0; - if (weap1 && item_cursed(*weap1)) + if (weap1 && weap1->cursed()) num_cursed++; - if (weap2 && item_cursed(*weap2)) + if (weap2 && weap2->cursed()) num_cursed++; - if (_shield && item_cursed(*_shield)) + if (_shield && _shield->cursed()) num_cursed++; if (two_handed && num_cursed > 0 || num_cursed >= avail_slots) diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index 4e5cf2dc77..04c1498297 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -165,7 +165,7 @@ bool move_player_to_grid( const coord_def& p, bool stepped, bool allow_shift, { std::string prompt = make_stringf( "Really step into that cloud of %s?", - cloud_name(ctype).c_str()); + cloud_name(cloud).c_str()); if (!yesno(prompt.c_str(), false, 'n')) { @@ -5732,7 +5732,7 @@ bool player::can_wield(const item_def& item, bool ignore_curse, { if (equip[EQ_WEAPON] != -1 && !ignore_curse) { - if (item_cursed(inv[equip[EQ_WEAPON]])) + if (inv[equip[EQ_WEAPON]].cursed()) return (false); } @@ -7031,6 +7031,17 @@ void player::moveto(const coord_def &c) set_position(c); } +bool player::move_to_pos(const coord_def &c) +{ + actor *target = actor_at(c); + if (!target || target->submerged()) + { + moveto(c); + return true; + } + return false; +} + void player::shiftto(const coord_def &c) { crawl_view.shift_player_to(c); @@ -7423,6 +7434,3 @@ void player::set_duration(duration_type dur, int turns, you.duration[dur] = 0; increase_duration(dur, turns, cap, msg); } - - - diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h index d7f5306c08..a02e5f5c4c 100644 --- a/crawl-ref/source/player.h +++ b/crawl-ref/source/player.h @@ -314,6 +314,7 @@ public: void set_position(const coord_def &c); // Low-level move the player. Use this instead of changing pos directly. void moveto(const coord_def &c); + bool move_to_pos(const coord_def &c); // Move the player during an abyss shift. void shiftto(const coord_def &c); bool blink_to(const coord_def& c, bool quiet = false); diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc index 395db6af89..763f1c0709 100644 --- a/crawl-ref/source/religion.cc +++ b/crawl-ref/source/religion.cc @@ -609,7 +609,7 @@ std::string get_god_likes(god_type which_god, bool verbose) break; } - if (god_likes_fresh_corpses(which_god)) + if (god_likes_fresh_corpses(which_god) && which_god != GOD_KIKUBAAQUDGHA) { snprintf(info, INFO_SIZE, "you sacrifice fresh corpses%s", verbose ? " (by standing over them and <w>p</w>raying)" : ""); @@ -4230,7 +4230,7 @@ static bool _bless_weapon(god_type god, brand_type brand, int colour) set_item_ego_type(wpn, OBJ_WEAPONS, brand); wpn.colour = colour; - const bool is_cursed = item_cursed(wpn); + const bool is_cursed = wpn.cursed(); enchant_weapon(ENCHANT_TO_HIT, true, wpn); diff --git a/crawl-ref/source/rltiles/dc-mon.txt b/crawl-ref/source/rltiles/dc-mon.txt index ef0836c80b..b4a0c59fdf 100644 --- a/crawl-ref/source/rltiles/dc-mon.txt +++ b/crawl-ref/source/rltiles/dc-mon.txt @@ -339,6 +339,7 @@ vault_guard MONS_VAULT_GUARD shapeshifter MONS_SHAPESHIFTER glowing_shapeshifter MONS_GLOWING_SHAPESHIFTER killer_klown MONS_KILLER_KLOWN +slave MONS_SLAVE ## From the Wucad Mu wizlab human_monk_ghost MONS_HUMAN_MONK ## From the Cigotuvi wizlab diff --git a/crawl-ref/source/rltiles/dc-mon/slave.png b/crawl-ref/source/rltiles/dc-mon/slave.png Binary files differnew file mode 100644 index 0000000000..0f7e353e5c --- /dev/null +++ b/crawl-ref/source/rltiles/dc-mon/slave.png diff --git a/crawl-ref/source/rltiles/dc-mon/unique/maurice.png b/crawl-ref/source/rltiles/dc-mon/unique/maurice.png Binary files differindex 284ad4645c..874963221c 100644 --- a/crawl-ref/source/rltiles/dc-mon/unique/maurice.png +++ b/crawl-ref/source/rltiles/dc-mon/unique/maurice.png diff --git a/crawl-ref/source/show.cc b/crawl-ref/source/show.cc index d6c3ff577a..c37901d7e0 100644 --- a/crawl-ref/source/show.cc +++ b/crawl-ref/source/show.cc @@ -273,8 +273,7 @@ void show_def::_update_cloud(int cloudno) grid(e).colour = which_colour; #ifdef USE_TILE - tile_place_cloud(e.x, e.y, env.cloud[cloudno].type, - env.cloud[cloudno].decay); + tile_place_cloud(e.x, e.y, env.cloud[cloudno]); #endif } diff --git a/crawl-ref/source/spells1.cc b/crawl-ref/source/spells1.cc index 647077f2f4..f6dd4009a3 100644 --- a/crawl-ref/source/spells1.cc +++ b/crawl-ref/source/spells1.cc @@ -661,29 +661,32 @@ bool stinking_cloud( int pow, bolt &beem ) int cast_big_c(int pow, cloud_type cty, kill_category whose, bolt &beam) { - big_cloud( cty, whose, beam.target, pow, 8 + random2(3) ); + big_cloud( cty, whose, beam.target, pow, 8 + random2(3), -1 ); return (1); } void big_cloud(cloud_type cl_type, kill_category whose, - const coord_def& where, int pow, int size, int spread_rate) + const coord_def& where, int pow, int size, int spread_rate, + int colour, std::string name, std::string tile) { big_cloud(cl_type, whose, cloud_struct::whose_to_killer(whose), - where, pow, size, spread_rate); + where, pow, size, spread_rate, colour, name, tile); } void big_cloud(cloud_type cl_type, killer_type killer, - const coord_def& where, int pow, int size, int spread_rate) + const coord_def& where, int pow, int size, int spread_rate, + int colour, std::string name, std::string tile) { big_cloud(cl_type, cloud_struct::killer_to_whose(killer), killer, - where, pow, size, spread_rate); + where, pow, size, spread_rate, colour, name, tile); } void big_cloud(cloud_type cl_type, kill_category whose, killer_type killer, - const coord_def& where, int pow, int size, int spread_rate) + const coord_def& where, int pow, int size, int spread_rate, + int colour, std::string name, std::string tile) { apply_area_cloud(make_a_normal_cloud, where, pow, size, - cl_type, whose, killer, spread_rate); + cl_type, whose, killer, spread_rate, colour, name, tile); } static bool _mons_hostile(const monsters *mon) diff --git a/crawl-ref/source/spells1.h b/crawl-ref/source/spells1.h index 8368947f03..b342b71684 100644 --- a/crawl-ref/source/spells1.h +++ b/crawl-ref/source/spells1.h @@ -21,11 +21,14 @@ void remove_divine_stamina(); bool cast_vitalisation(); void big_cloud(cloud_type cl_type, kill_category whose, const coord_def& where, - int pow, int size, int spread_rate = -1); + int pow, int size, int spread_rate = -1, int colour = -1, + std::string name = "", std::string tile = ""); void big_cloud(cloud_type cl_type, killer_type killer, const coord_def& where, - int pow, int size, int spread_rate = -1); + int pow, int size, int spread_rate = -1, int colour = -1, + std::string name = "", std::string tile = ""); void big_cloud(cloud_type cl_type, kill_category whose, killer_type killer, - const coord_def& where, int pow, int size, int spread_rate = -1); + const coord_def& where, int pow, int size, int spread_rate = -1, + int colour = -1, std::string name = "", std::string tile = ""); int blink(int pow, bool high_level_controlled_blink, bool wizard_blink = false); diff --git a/crawl-ref/source/spells2.cc b/crawl-ref/source/spells2.cc index 4902786aa6..d765d4db5f 100644 --- a/crawl-ref/source/spells2.cc +++ b/crawl-ref/source/spells2.cc @@ -1101,7 +1101,7 @@ bool cast_sticks_to_snakes(int pow, god_type god) const int dur = std::min(3 + random2(pow) / 20, 5); int how_many_max = 1 + random2(1 + you.skills[SK_TRANSMUTATIONS]) / 4; - const bool friendly = (!item_cursed(wpn)); + const bool friendly = (!wpn.cursed()); const beh_type beha = (friendly) ? BEH_FRIENDLY : BEH_HOSTILE; int count = 0; @@ -1712,7 +1712,7 @@ bool cast_tukimas_dance(int pow, god_type god, bool force_hostile) if (success) { // Cursed weapons become hostile. - const bool friendly = (!force_hostile && !item_cursed(you.inv[wpn])); + const bool friendly = (!force_hostile && !you.inv[wpn].cursed()); mgen_data mg(MONS_DANCING_WEAPON, friendly ? BEH_FRIENDLY : BEH_HOSTILE, diff --git a/crawl-ref/source/spells3.cc b/crawl-ref/source/spells3.cc index 26747c1f07..1a30607199 100644 --- a/crawl-ref/source/spells3.cc +++ b/crawl-ref/source/spells3.cc @@ -130,7 +130,7 @@ bool remove_curse(bool suppress_msg) // Only cursed *weapons* in hand count as cursed. - bwr if (you.weapon() && you.weapon()->base_type == OBJ_WEAPONS - && item_cursed(*you.weapon())) + && you.weapon()->cursed()) { // Also sets wield_change. do_uncurse_item(*you.weapon()); @@ -142,7 +142,7 @@ bool remove_curse(bool suppress_msg) for (int i = EQ_WEAPON + 1; i < NUM_EQUIP; i++) { // Melded equipment can also get uncursed this way. - if (you.equip[i] != -1 && item_cursed(you.inv[you.equip[i]])) + if (you.equip[i] != -1 && you.inv[you.equip[i]].cursed()) { do_uncurse_item(you.inv[you.equip[i]]); success = true; @@ -1528,13 +1528,15 @@ static bool _teleport_player(bool allow_control, bool new_abyss_area, bool wizar if (is_controlled) { - mpr("You may choose your destination (press '.' or delete to select)."); - mpr("Expect minor deviation."); check_ring_TC = true; - // Only have the more prompt for non-wizard. + // Only have the messages and the more prompt for non-wizard. if (!wizard_tele) + { + mpr("You may choose your destination (press '.' or delete to select)."); + mpr("Expect minor deviation."); more(); + } while (true) { diff --git a/crawl-ref/source/spells4.cc b/crawl-ref/source/spells4.cc index 6819599ee5..6fbb55f399 100644 --- a/crawl-ref/source/spells4.cc +++ b/crawl-ref/source/spells4.cc @@ -879,14 +879,15 @@ void cast_dispersal(int pow) int make_a_normal_cloud(coord_def where, int pow, int spread_rate, cloud_type ctype, kill_category whose, - killer_type killer) + killer_type killer, int colour, std::string name, + std::string tile) { if (killer == KILL_NONE) killer = cloud_struct::whose_to_killer(whose); place_cloud( ctype, where, (3 + random2(pow / 4) + random2(pow / 4) + random2(pow / 4)), - whose, killer, spread_rate ); + whose, killer, spread_rate, colour, name, tile ); return 1; } diff --git a/crawl-ref/source/spells4.h b/crawl-ref/source/spells4.h index fbadac0203..cd977e3d01 100644 --- a/crawl-ref/source/spells4.h +++ b/crawl-ref/source/spells4.h @@ -16,7 +16,8 @@ struct bolt; bool backlight_monsters(coord_def where, int pow, int garbage); int make_a_normal_cloud(coord_def where, int pow, int spread_rate, cloud_type ctype, kill_category, - killer_type killer = KILL_NONE); + killer_type killer = KILL_NONE, int colour = -1, + std::string name = "", std::string tile = ""); int disperse_monsters(coord_def where, int pow); void remove_condensation_shield(); diff --git a/crawl-ref/source/spl-util.cc b/crawl-ref/source/spl-util.cc index 2f32ea67e1..75d7264e07 100644 --- a/crawl-ref/source/spl-util.cc +++ b/crawl-ref/source/spl-util.cc @@ -73,7 +73,8 @@ static struct spell_desc *_seekspell(spell_type spellid); static bool _cloud_helper(cloud_func func, const coord_def& where, int pow, int spread_rate, cloud_type ctype, kill_category whose, - killer_type killer); + killer_type killer, int colour, + std::string name, std::string tile); // // BEGIN PUBLIC FUNCTIONS @@ -677,13 +678,14 @@ int apply_area_within_radius(cell_func cf, const coord_def& where, void apply_area_cloud( cloud_func func, const coord_def& where, int pow, int number, cloud_type ctype, kill_category whose, killer_type killer, - int spread_rate ) + int spread_rate, int colour, std::string name, + std::string tile) { int good_squares = 0; int neighbours[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; if (number && _cloud_helper(func, where, pow, spread_rate, ctype, whose, - killer)) + killer, colour, name, tile)) number--; if (number == 0) @@ -703,7 +705,8 @@ void apply_area_cloud( cloud_func func, const coord_def& where, { const int aux = arrs[m][i]; if ( _cloud_helper(func, where + Compass[aux], - pow, spread_rate, ctype, whose, killer)) + pow, spread_rate, ctype, whose, killer, colour, + name, tile)) { number--; good_squares++; @@ -729,7 +732,7 @@ void apply_area_cloud( cloud_func func, const coord_def& where, number -= spread; good_squares--; apply_area_cloud(func, where + Compass[j], pow, spread, ctype, whose, - killer, spread_rate); + killer, spread_rate, colour, name, tile); } } @@ -893,11 +896,13 @@ bool is_valid_spell(spell_type spell) static bool _cloud_helper(cloud_func func, const coord_def& where, int pow, int spread_rate, cloud_type ctype, kill_category whose, - killer_type killer) + killer_type killer, int colour, std::string name, + std::string tile) { if (!feat_is_solid(grd(where)) && env.cgrid(where) == EMPTY_CLOUD) { - func(where, pow, spread_rate, ctype, whose, killer); + func(where, pow, spread_rate, ctype, whose, killer, colour, name, + tile); return (true); } diff --git a/crawl-ref/source/spl-util.h b/crawl-ref/source/spl-util.h index 8ff8e4ceeb..eb44f4db07 100644 --- a/crawl-ref/source/spl-util.h +++ b/crawl-ref/source/spl-util.h @@ -80,7 +80,8 @@ const char* spelltype_long_name( int which_spelltype ); typedef int cell_func(coord_def where, int pow, int aux, actor *agent); typedef int cloud_func(coord_def where, int pow, int spreadrate, cloud_type type, kill_category whose, - killer_type killer); + killer_type killer, int colour, std::string name, + std::string tile); int apply_area_visible(cell_func cf, int power, bool pass_through_trans = false, actor *agent = NULL); @@ -104,7 +105,8 @@ int apply_area_within_radius(cell_func cf, const coord_def& where, void apply_area_cloud(cloud_func func, const coord_def& where, int pow, int number, cloud_type ctype, kill_category kc, killer_type killer, - int spread_rate = -1); + int spread_rate = -1, int colour = -1, + std::string name = "", std::string tile = ""); bool spell_direction( dist &spelld, bolt &pbolt, targetting_type restrict = DIR_NONE, diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc index ab60932b4f..6d12c323ab 100644 --- a/crawl-ref/source/tags.cc +++ b/crawl-ref/source/tags.cc @@ -61,6 +61,7 @@ #include "artefact.h" #include "branch.h" +#include "coordit.h" #include "describe.h" #include "dungeon.h" #include "enum.h" @@ -1802,6 +1803,9 @@ static void tag_construct_level(writer &th) marshallByte(th, (char) env.cloud[i].spread_rate); marshallByte(th, env.cloud[i].whose); marshallByte(th, env.cloud[i].killer); + marshallShort(th, env.cloud[i].colour); + marshallString(th, env.cloud[i].name); + marshallString(th, env.cloud[i].tile); } // how many shops? @@ -1825,6 +1829,15 @@ static void tag_construct_level(writer &th) env.markers.write(th); env.properties.write(th); + + // Save heightmap, if present. + marshallByte(th, !!env.heightmap.get()); + if (env.heightmap.get()) + { + grid_heightmap &heightmap(*env.heightmap); + for (rectangle_iterator ri(0); ri; ++ri) + marshallShort(th, heightmap(*ri)); + } } void marshallItem(writer &th, const item_def &item) @@ -2191,6 +2204,9 @@ static void tag_read_level( reader &th, char minorVersion ) env.cloud[i].spread_rate = (unsigned char) unmarshallByte(th); env.cloud[i].whose = static_cast<kill_category>(unmarshallByte(th)); env.cloud[i].killer = static_cast<killer_type>(unmarshallByte(th)); + env.cloud[i].colour = unmarshallShort(th); + env.cloud[i].name = unmarshallString(th); + env.cloud[i].tile = unmarshallString(th); } // how many shops? @@ -2217,6 +2233,17 @@ static void tag_read_level( reader &th, char minorVersion ) env.properties.clear(); env.properties.read(th); + + // Restore heightmap + env.heightmap.reset(NULL); + const bool have_heightmap(unmarshallByte(th)); + if (have_heightmap) + { + env.heightmap.reset(new grid_heightmap); + grid_heightmap &heightmap(*env.heightmap); + for (rectangle_iterator ri(0); ri; ++ri) + heightmap(*ri) = unmarshallShort(th); + } } static void tag_read_level_items(reader &th, char minorVersion) diff --git a/crawl-ref/source/tags.h b/crawl-ref/source/tags.h index 26ecf81233..f9c3959299 100644 --- a/crawl-ref/source/tags.h +++ b/crawl-ref/source/tags.h @@ -40,14 +40,14 @@ enum tag_file_type // file types supported by tag system enum tag_major_version { TAG_MAJOR_START = 5, - TAG_MAJOR_VERSION = 11 + TAG_MAJOR_VERSION = 12 }; // Minor version will be reset to zero when major version changes. enum tag_minor_version { - TAG_MINOR_RESET = 0, // Minor tags were reset - TAG_MINOR_VERSION = 0 // Current version. (Keep equal to max.) + TAG_MINOR_RESET = 0, // Minor tags were reset + TAG_MINOR_VERSION = 0 // Current version. (Keep equal to max.) }; struct enum_info diff --git a/crawl-ref/source/terrain.cc b/crawl-ref/source/terrain.cc index 05b3033e8e..9b2fe96deb 100644 --- a/crawl-ref/source/terrain.cc +++ b/crawl-ref/source/terrain.cc @@ -664,7 +664,7 @@ static void _dgn_check_terrain_items(const coord_def &pos, bool preserve_items) item = mitm[item].link; // Game-critical item. - if (preserve_items || item_is_critical(mitm[curr])) + if (preserve_items || mitm[curr].is_critical()) _dgn_shift_item(pos, mitm[curr]); else { diff --git a/crawl-ref/source/tilepick.cc b/crawl-ref/source/tilepick.cc index 725947c13c..34cbf7eb7f 100644 --- a/crawl-ref/source/tilepick.cc +++ b/crawl-ref/source/tilepick.cc @@ -764,6 +764,8 @@ int tileidx_monster_base(const monsters *mon, bool detected) return TILEP_MONS_GLOWING_SHAPESHIFTER; case MONS_KILLER_KLOWN: return TILEP_MONS_KILLER_KLOWN; + case MONS_SLAVE: + return TILEP_MONS_SLAVE; // mimics case MONS_GOLD_MIMIC: @@ -2647,64 +2649,89 @@ int tileidx_feature(dungeon_feature_type feat, int gx, int gy) } } -static int _tileidx_cloud(int type, int decay) +static int _tileidx_cloud(cloud_struct cl) { + int type = cl.type; + int decay = cl.decay; + std::string override = cl.tile; + int colour = cl.colour; + int ch = TILE_ERROR; int dur = decay/20; if (dur > 2) dur = 2; - switch (type) + if (!override.empty()) { - case CLOUD_FIRE: - ch = TILE_CLOUD_FIRE_0 + dur; - break; + unsigned int index; + if (!tile_main_index(override.c_str(), index)) + { + mprf(MSGCH_ERROR, "Invalid tile requested for cloud: '%s'.", override.c_str()); + } + else + { + int offset = tile_main_count(index); + ch = index + offset; + } + } + else + { + switch (type) + { + case CLOUD_FIRE: + ch = TILE_CLOUD_FIRE_0 + dur; + break; - case CLOUD_COLD: - ch = TILE_CLOUD_COLD_0 + dur; - break; + case CLOUD_COLD: + ch = TILE_CLOUD_COLD_0 + dur; + break; - case CLOUD_STINK: - case CLOUD_POISON: - ch = TILE_CLOUD_POISON_0 + dur; - break; + case CLOUD_STINK: + case CLOUD_POISON: + ch = TILE_CLOUD_POISON_0 + dur; + break; - case CLOUD_BLUE_SMOKE: - ch = TILE_CLOUD_BLUE_SMOKE; - break; + case CLOUD_BLUE_SMOKE: + ch = TILE_CLOUD_BLUE_SMOKE; + break; - case CLOUD_PURPLE_SMOKE: - case CLOUD_TLOC_ENERGY: - ch = TILE_CLOUD_TLOC_ENERGY; - break; + case CLOUD_PURPLE_SMOKE: + case CLOUD_TLOC_ENERGY: + ch = TILE_CLOUD_TLOC_ENERGY; + break; - case CLOUD_MIASMA: - ch = TILE_CLOUD_MIASMA; - break; + case CLOUD_MIASMA: + ch = TILE_CLOUD_MIASMA; + break; - case CLOUD_BLACK_SMOKE: - ch = TILE_CLOUD_BLACK_SMOKE; - break; + case CLOUD_BLACK_SMOKE: + ch = TILE_CLOUD_BLACK_SMOKE; + break; - case CLOUD_MUTAGENIC: - ch = (dur == 0 ? TILE_CLOUD_MUTAGENIC_0 : - dur == 1 ? TILE_CLOUD_MUTAGENIC_1 - : TILE_CLOUD_MUTAGENIC_2); - ch += random2(tile_main_count(ch)); - break; + case CLOUD_MUTAGENIC: + ch = (dur == 0 ? TILE_CLOUD_MUTAGENIC_0 : + dur == 1 ? TILE_CLOUD_MUTAGENIC_1 + : TILE_CLOUD_MUTAGENIC_2); + ch += random2(tile_main_count(ch)); + break; - case CLOUD_MIST: - ch = TILE_CLOUD_MIST; - break; + case CLOUD_MIST: + ch = TILE_CLOUD_MIST; + break; - case CLOUD_RAIN: - ch = TILE_CLOUD_RAIN + random2(tile_main_count(TILE_CLOUD_RAIN)); - break; + case CLOUD_RAIN: + ch = TILE_CLOUD_RAIN + random2(tile_main_count(TILE_CLOUD_RAIN)); + break; - default: - ch = TILE_CLOUD_GREY_SMOKE; - break; + default: + ch = TILE_CLOUD_GREY_SMOKE; + break; + } } + + if (colour != -1) + ch = tile_main_coloured(ch, colour); + return (ch | TILE_FLAG_FLYING); } @@ -4850,9 +4877,9 @@ void tile_place_monster(int gx, int gy, int idx, bool foreground, bool detected) } } -void tile_place_cloud(int x, int y, int type, int decay) +void tile_place_cloud(int x, int y, cloud_struct cl) { - env.tile_fg[x][y] = _tileidx_cloud(type, decay); + env.tile_fg[x][y] = _tileidx_cloud(cl); } unsigned int num_tile_rays = 0; diff --git a/crawl-ref/source/tilereg.cc b/crawl-ref/source/tilereg.cc index 753ea5be30..4490e3a59a 100644 --- a/crawl-ref/source/tilereg.cc +++ b/crawl-ref/source/tilereg.cc @@ -1541,10 +1541,8 @@ int DungeonRegion::handle_mouse(MouseEvent &event) const int cloudidx = env.cgrid(gc); if (cloudidx != EMPTY_CLOUD) { - cloud_type ctype = env.cloud[cloudidx].type; - std::string terrain_desc = desc; - desc = cloud_name(ctype); + desc = cloud_name(cloudidx); if (!terrain_desc.empty()) desc += "\n" + terrain_desc; @@ -1937,9 +1935,7 @@ bool DungeonRegion::update_alt_text(std::string &alt) const int cloudidx = env.cgrid(gc); if (cloudidx != EMPTY_CLOUD) { - cloud_type ctype = env.cloud[cloudidx].type; - - inf.prefix = "There is a cloud of " + cloud_name(ctype) + inf.prefix = "There is a cloud of " + cloud_name(cloudidx) + " here.$$"; } } @@ -2488,7 +2484,7 @@ static bool _can_use_item(const item_def &item, bool equipped) && mons_has_blood(item.plus)); } - if (equipped && item_cursed(item)) + if (equipped && item.cursed()) { // Misc. items/rods can always be evoked, cursed or not. if (item.base_type == OBJ_MISCELLANY || item_is_rod(item)) @@ -2774,7 +2770,7 @@ bool InventoryRegion::update_tip_text(std::string& tip) tip += "\n[R-Click] Info"; // Has to be non-equipped or non-cursed to drop. if (!equipped || !_is_true_equipped_item(you.inv[idx]) - || !item_cursed(you.inv[idx])) + || !you.inv[idx].cursed()) { tip += "\n[Shift-L-Click] Drop (d)"; } diff --git a/crawl-ref/source/tiles.h b/crawl-ref/source/tiles.h index d938715e55..2f1028b72d 100644 --- a/crawl-ref/source/tiles.h +++ b/crawl-ref/source/tiles.h @@ -74,7 +74,7 @@ void tile_place_monster(int gx, int gy, int idx, bool foreground = true, bool detected = false); void tile_place_item(int x, int y, int idx); void tile_place_item_marker(int x, int y, int idx); -void tile_place_cloud(int x, int y, int type, int decay); +void tile_place_cloud(int x, int y, cloud_struct cl); void tile_place_ray(const coord_def& gc, bool in_range); void tile_draw_rays(bool resetCount); void tile_clear_buf(); diff --git a/crawl-ref/source/tilesdl.cc b/crawl-ref/source/tilesdl.cc index e3c1e978fd..50dbb83f58 100644 --- a/crawl-ref/source/tilesdl.cc +++ b/crawl-ref/source/tilesdl.cc @@ -1433,7 +1433,7 @@ static void _fill_item_info(InventoryTile &desc, const item_def &item) desc.special = tile_known_weapon_brand(item); desc.flag = 0; - if (item_cursed(item) && item_ident(item, ISFLAG_KNOW_CURSE)) + if (item.cursed() && item_ident(item, ISFLAG_KNOW_CURSE)) desc.flag |= TILEI_FLAG_CURSE; if (item_type_tried(item)) desc.flag |= TILEI_FLAG_TRIED; diff --git a/crawl-ref/source/transfor.cc b/crawl-ref/source/transfor.cc index 39f932a5eb..eaa1d12afb 100644 --- a/crawl-ref/source/transfor.cc +++ b/crawl-ref/source/transfor.cc @@ -311,7 +311,7 @@ static bool _check_for_cursed_equipment(const std::set<equipment_type> &remove, continue; const item_def& item = you.inv[ you.equip[e] ]; - if (item_cursed(item)) + if (item.cursed()) { if (e != EQ_WEAPON && _tran_may_meld_cursed(trans)) continue; diff --git a/crawl-ref/source/trap_def.h b/crawl-ref/source/trap_def.h index 90c2cc4372..2ec924d3b4 100644 --- a/crawl-ref/source/trap_def.h +++ b/crawl-ref/source/trap_def.h @@ -18,6 +18,7 @@ struct trap_def void prepare_ammo(); bool type_has_ammo() const; bool active() const; + int max_damage(const actor& act); private: void message_trap_entry(); diff --git a/crawl-ref/source/traps.cc b/crawl-ref/source/traps.cc index e8b8d92bce..b7ca9f2dec 100644 --- a/crawl-ref/source/traps.cc +++ b/crawl-ref/source/traps.cc @@ -677,41 +677,41 @@ void trap_def::trigger(actor& triggerer, bool flat_footed) this->destroy(); } -int trap_def::shot_damage(actor& act) +int trap_def::max_damage(const actor& act) { - if (act.atype() == ACT_PLAYER) - { - switch (this->type) - { - case TRAP_NEEDLE: return 0; - case TRAP_DART: return random2( 4 + you.your_level/2) + 1; - case TRAP_ARROW: return random2( 7 + you.your_level) + 1; - case TRAP_SPEAR: return random2(10 + you.your_level) + 1; - case TRAP_BOLT: return random2(13 + you.your_level) + 1; - case TRAP_AXE: return random2(15 + you.your_level) + 1; - default: return 0; - } - } - else if (act.atype() == ACT_MONSTER) + int level = you.your_level; + + // Trap damage to monsters is not a function of level, because + // they are fairly stupid and tend to have fewer hp than + // players -- this choice prevents traps from easily killing + // large monsters fairly deep within the dungeon. + if (act.atype() == ACT_MONSTER) + level = 0; + + switch (this->type) { - // Trap damage to monsters is not a function of level, because - // they are fairly stupid and tend to have fewer hp than - // players -- this choice prevents traps from easily killing - // large monsters fairly deep within the dungeon. - switch (this->type) - { - case TRAP_NEEDLE: return 0; - case TRAP_DART: return random2( 4) + 1; - case TRAP_ARROW: return random2( 7) + 1; - case TRAP_SPEAR: return random2(10) + 1; - case TRAP_BOLT: return random2(13) + 1; - case TRAP_AXE: return random2(15) + 1; - default: return 0; - } + case TRAP_NEEDLE: return 0; + case TRAP_DART: return 4 + level/2; + case TRAP_ARROW: return 7 + level; + case TRAP_SPEAR: return 10 + level; + case TRAP_BOLT: return 13 + level; + case TRAP_AXE: return 15 + level; + case TRAP_BLADE: return (level ? 2*level : 10) + 28; + default: return 0; } + return (0); } +int trap_def::shot_damage(actor& act) +{ + const int dam = max_damage(act); + + if (!dam) + return 0; + return random2(dam) + 1; +} + int reveal_traps(const int range) { int traps_found = 0; @@ -756,6 +756,15 @@ trap_type get_trap_type(const coord_def& pos) return (TRAP_UNASSIGNED); } +static bool _disarm_is_deadly(trap_def& trap) +{ + int dam = trap.max_damage(you); + if (trap.type == TRAP_NEEDLE && you.res_poison() <= 0) + dam += 15; // arbitrary + + return (you.hp <= dam); +} + // where *must* point to a valid, discovered trap. void disarm_trap(const coord_def& where) { @@ -780,11 +789,8 @@ void disarm_trap(const coord_def& where) break; } -#ifdef CLUA_BINDINGS // Prompt for any trap for which you might not survive setting it off. - // (See trapwalk.lua) - if (Options.trap_prompt - && !clua.callbooleanfn(false, "ch_cross_trap", "s", trap_name(where))) + if (_disarm_is_deadly(trap)) { std::string prompt = make_stringf( "Really try disarming that %s?", @@ -799,7 +805,6 @@ void disarm_trap(const coord_def& where) return; } } -#endif // Make the actual attempt you.turn_is_over = true; diff --git a/crawl-ref/source/tutorial.cc b/crawl-ref/source/tutorial.cc index 6893f64262..69aeb760a2 100644 --- a/crawl-ref/source/tutorial.cc +++ b/crawl-ref/source/tutorial.cc @@ -1549,7 +1549,7 @@ static bool _cant_butcher() if (!wpn || wpn->base_type != OBJ_WEAPONS) return false; - return (item_cursed(*wpn) && !can_cut_meat(*wpn)); + return (wpn->cursed() && !can_cut_meat(*wpn)); } static int _num_butchery_tools() @@ -3012,7 +3012,7 @@ void learned_something_new(tutorial_event_type seen_what, coord_def gc) int wpn = you.equip[EQ_WEAPON]; if (wpn != -1 && you.inv[wpn].base_type == OBJ_WEAPONS - && item_cursed(you.inv[wpn])) + && you.inv[wpn].cursed()) { // Don't trigger if the wielded weapon is cursed. Tutorial.tutorial_events[seen_what] = true; @@ -4440,7 +4440,7 @@ static void _tutorial_describe_cloud(int x, int y) if (ctype == CLOUD_NONE) return; - std::string cname = cloud_name(ctype); + std::string cname = cloud_name(env.cgrid(coord_def(x, y))); std::ostringstream ostr; diff --git a/crawl-ref/source/viewchar.cc b/crawl-ref/source/viewchar.cc index 78818642bd..bf0460591c 100644 --- a/crawl-ref/source/viewchar.cc +++ b/crawl-ref/source/viewchar.cc @@ -24,27 +24,35 @@ static const unsigned dchar_table[ NUM_CSET ][ NUM_DCHAR_TYPES ] = 177, 176, 249, 250, '\'', 254, '^', '>', '<', // wall .. stairs up 220, 239, 244, 247, '8', '~', '~', // altar .. item detect '0', ')', '[', '/', '%', '?', '=', '!', '(', // orb .. missile - '+', '\\', '}', '%', '$', '"', '#', 234, // book .. trees + 236, '\\', '}', '%', '$', '"', '#', 234, // book .. trees ' ', '!', '#', '%', '+', ')', '*', '+', // space .. fired_burst '/', '=', '?', 'X', '[', '`', '#' // fi_stick .. explosion }, // CSET_DEC - remember: 224-255 are mapped to shifted 96-127 + // It's better known as "vt100 line drawing characters". { 225, 224, 254, ':', '\'', 238, '^', '>', '<', // wall .. stairs up 251, 182, 167, 187, '8', 171, 168, // altar .. item detect '0', ')', '[', '/', '%', '?', '=', '!', '(', // orb .. missile - '+', '\\', '}', '%', '$', '"', '#', '7', // book .. trees + ':', '\\', '}', '%', '$', '"', '#', '7', // book .. trees ' ', '!', '#', '%', '+', ')', '*', '+', // space .. fired_burst '/', '=', '?', 'X', '[', '`', '#' // fi_stick .. explosion }, // CSET_UNICODE + /* Beware, some popular terminals (PuTTY, xterm) are incapable of coping with + the lack of a character in the chosen font, and most popular fonts have a + quite limited repertoire. A subset that is reasonably likely to be present + is http://en.wikipedia.org/wiki/WGL4; we could provide a richer alternate + set for those on more capable terminals (including for example Thai 0xEB0 + for clouds), but that would require decoupling encoding from charset. + */ { 0x2592, 0x2591, 0xB7, 0x25E6, '\'', 0x25FC, '^', '>', '<', '_', 0x2229, 0x2320, 0x2248, '8', '~', '~', '0', ')', '[', '/', '%', '?', '=', '!', '(', - '+', '|', '}', '%', '$', '"', '#', 0x2663, + 0x221E, '|', '}', '%', '$', '"', '#', 0x2663, ' ', '!', '#', '%', '+', ')', '*', '+', // space .. fired_burst '/', '=', '?', 'X', '[', '`', '#' // fi_stick .. explosion }, diff --git a/crawl-ref/source/wiz-dgn.cc b/crawl-ref/source/wiz-dgn.cc index ea55dc9e56..8251e6c76e 100644 --- a/crawl-ref/source/wiz-dgn.cc +++ b/crawl-ref/source/wiz-dgn.cc @@ -269,70 +269,53 @@ void wizard_create_portal() } } -void wizard_create_feature_number() +void wizard_create_feature() { char specs[256]; int feat_num; - mpr("Create which feature (by number)? ", MSGCH_PROMPT); + dungeon_feature_type feat; + mpr("Create which feature? ", MSGCH_PROMPT); - if (!cancelable_get_line(specs, sizeof(specs)) - && (feat_num = atoi(specs))) + if (!cancelable_get_line(specs, sizeof(specs)) && specs[0] != 0) { - dungeon_feature_type feat = static_cast<dungeon_feature_type>(feat_num); - if (feat == DNGN_ENTER_SHOP) + if ((feat_num = atoi(specs))) { - debug_make_shop(); - return; + feat = static_cast<dungeon_feature_type>(feat_num); } - - dungeon_terrain_changed(you.pos(), feat, false); -#ifdef USE_TILE - env.tile_flv(you.pos()).special = 0; -#endif - } - else - canned_msg(MSG_OK); -} - -void wizard_create_feature_name() -{ - char specs[256]; - mpr("Create which feature (by name)? ", MSGCH_PROMPT); - if (!cancelable_get_line(specs, sizeof(specs)) && specs[0] != 0) - { - // Accept both "shallow_water" and "Shallow water" - std::string name = lowercase_string(specs); - name = replace_all(name, " ", "_"); - - dungeon_feature_type feat = dungeon_feature_by_name(name); - if (feat == DNGN_UNSEEN) // no exact match + else { - std::vector<std::string> matches = dungeon_feature_matches(name); - - if (matches.empty()) + std::string name = lowercase_string(specs); + name = replace_all(name, " ", "_"); + feat = dungeon_feature_by_name(name); + if (feat == DNGN_UNSEEN) // no exact match { - mprf(MSGCH_DIAGNOSTICS, "No features matching '%s'", - name.c_str()); - return; - } + std::vector<std::string> matches = dungeon_feature_matches(name); - // Only one possible match, use that. - if (matches.size() == 1) - { - name = matches[0]; - feat = dungeon_feature_by_name(name); - } - // Multiple matches, list them to wizard - else - { - std::string prefix = "No exact match for feature '" + - name + "', possible matches are: "; + if (matches.empty()) + { + mprf(MSGCH_DIAGNOSTICS, "No features matching '%s'", + name.c_str()); + return; + } - // Use mpr_comma_separated_list() because the list - // might be *LONG*. - mpr_comma_separated_list(prefix, matches, " and ", ", ", - MSGCH_DIAGNOSTICS); - return; + // Only one possible match, use that. + if (matches.size() == 1) + { + name = matches[0]; + feat = dungeon_feature_by_name(name); + } + // Multiple matches, list them to wizard + else + { + std::string prefix = "No exact match for feature '" + + name + "', possible matches are: "; + + // Use mpr_comma_separated_list() because the list + // might be *LONG*. + mpr_comma_separated_list(prefix, matches, " and ", ", ", + MSGCH_DIAGNOSTICS); + return; + } } } @@ -342,8 +325,6 @@ void wizard_create_feature_name() return; } - mprf(MSGCH_DIAGNOSTICS, "Setting (%d,%d) to %s (%d)", - you.pos().x, you.pos().y, name.c_str(), feat); dungeon_terrain_changed(you.pos(), feat, false); #ifdef USE_TILE env.tile_flv(you.pos()).special = 0; diff --git a/crawl-ref/source/wiz-dgn.h b/crawl-ref/source/wiz-dgn.h index c8561dcb1f..3db62ceb1b 100644 --- a/crawl-ref/source/wiz-dgn.h +++ b/crawl-ref/source/wiz-dgn.h @@ -10,8 +10,7 @@ #include <string> void wizard_create_portal(); -void wizard_create_feature_number(); -void wizard_create_feature_name(); +void wizard_create_feature(); void wizard_list_branches(); void wizard_reveal_traps(); void wizard_map_level(); diff --git a/crawl-ref/source/wiz-item.cc b/crawl-ref/source/wiz-item.cc index b4b94bb94f..6fa4b411b0 100644 --- a/crawl-ref/source/wiz-item.cc +++ b/crawl-ref/source/wiz-item.cc @@ -697,7 +697,7 @@ void wizard_uncurse_item() { item_def& item(you.inv[i]); - if (item_cursed(item)) + if (item.cursed()) do_uncurse_item(item); else if (_item_type_can_be_cursed(item.base_type)) do_curse_item(item); diff --git a/crawl-ref/source/xom.cc b/crawl-ref/source/xom.cc index a2c7a4d227..4f01f1145a 100644 --- a/crawl-ref/source/xom.cc +++ b/crawl-ref/source/xom.cc @@ -2308,7 +2308,7 @@ static void _xom_zero_miscast() { const item_def &item(you.inv[i]); if (item.is_valid() && !item_is_equipped(item) - && !item_is_critical(item)) + && !item.is_critical()) { inv_items.push_back(i); } |