summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
authorj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2008-05-25 22:36:55 +0000
committerj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2008-05-25 22:36:55 +0000
commita81f929851dd78e52d4748835ac33e082e945992 (patch)
tree6e10da17c848082f2904b46553f96cb23692052d /crawl-ref
parentf07d0af2207210a792aa3681b1c98ccb0b513baa (diff)
downloadcrawl-ref-a81f929851dd78e52d4748835ac33e082e945992.tar.gz
crawl-ref-a81f929851dd78e52d4748835ac33e082e945992.zip
Fix aborting unchivalric attacks costing a turn.
Implement ordering your friends to stay where they are. To do this, I've added a new variable to the monster struct: patrol_point, that is set by the new t sub-command "Wait here!" Once this is set, monsters will spend their time wandering around within the LOS radius centred on the patrol point. If they are attacked, or the player or other friends are attacked, they'll stop wandering to fight, but once the foe is gone, they continue doing so. Currently, the only way to make them stop again is to issue another command, "Follow me!" that is basically the already existing "Come here!" command. I've also added a "Stop fighting!" command that for non-patrolling monsters has the same effect as "Follow me!" - patrolling monsters are supposed to take up their wanderings again. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@5247 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/acr.cc4
-rw-r--r--crawl-ref/source/dungeon.cc64
-rw-r--r--crawl-ref/source/effects.cc94
-rw-r--r--crawl-ref/source/externs.h2
-rw-r--r--crawl-ref/source/fight.cc62
-rw-r--r--crawl-ref/source/it_use3.cc10
-rw-r--r--crawl-ref/source/mon-util.cc37
-rw-r--r--crawl-ref/source/monplace.h13
-rw-r--r--crawl-ref/source/monstuff.cc313
-rw-r--r--crawl-ref/source/tags.cc5
-rw-r--r--crawl-ref/source/tags.h3
-rw-r--r--crawl-ref/source/travel.cc4
12 files changed, 376 insertions, 235 deletions
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index 06f82d291d..397abc48dd 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -3516,8 +3516,8 @@ static void _open_door(int move_x, int move_y, bool check_confused)
}
- you_attack(mgrd[dx][dy], true);
you.turn_is_over = true;
+ you_attack(mgrd[dx][dy], true);
if (you.berserk_penalty != NO_BERSERK_PENALTY)
you.berserk_penalty = 0;
@@ -4075,8 +4075,8 @@ static void _move_player(int move_x, int move_y)
// an invisible monster attacks the monster, thus allowing
// the player to figure out which adjacent wall an invis
// monster is in "for free".
- you_attack( targ_monst, true );
you.turn_is_over = true;
+ you_attack( targ_monst, true );
// We don't want to create a penalty if there isn't
// supposed to be one.
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 9e7cbe1c76..c87f158c14 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -1789,8 +1789,8 @@ static bool _make_box(int room_x1, int room_y1, int room_x2, int room_y2,
return true;
}
-// take care of labyrinth, abyss, pandemonium
-// returns 1 if we should skip further generation,
+// Take care of labyrinth, abyss, pandemonium.
+// Returns 1 if we should skip further generation,
// -1 if we should immediately quit, and 0 otherwise.
static builder_rc_type _builder_by_type(int level_number, char level_type)
{
@@ -2874,9 +2874,9 @@ static int _place_monster_vector(std::vector<monster_type> montypes,
int result = 0;
mgen_data mg;
- mg.power = level_number;
+ mg.power = level_number;
mg.behaviour = BEH_SLEEP;
- mg.flags |= MG_PERMIT_BANDS;
+ mg.flags |= MG_PERMIT_BANDS;
mg.map_mask |= MMT_NO_MONS;
for (int i = 0; i < num_to_place; i++)
@@ -2962,8 +2962,8 @@ static void _builder_monsters(int level_number, char level_type, int mon_wanted)
for (int i = 0; i < mon_wanted; i++)
{
mgen_data mg;
- mg.power = level_number;
- mg.flags |= MG_PERMIT_BANDS;
+ mg.power = level_number;
+ mg.flags |= MG_PERMIT_BANDS;
mg.map_mask |= MMT_NO_MONS;
place_monster(mg);
@@ -3191,9 +3191,9 @@ static void _fill_monster_pit( spec_room &sr, FixedVector<pit_mons_def,
if (lord_type != MONS_PROGRAM_BUG)
{
mgen_data mg;
- mg.cls = lord_type;
+ mg.cls = lord_type;
mg.behaviour = BEH_SLEEP;
- mg.pos = coord_def(lordx, lordy);
+ mg.pos = coord_def(lordx, lordy);
mons_place(
mgen_data::sleeper_at(lord_type, coord_def(lordx, lordy)));
@@ -3218,9 +3218,8 @@ static void _fill_monster_pit( spec_room &sr, FixedVector<pit_mons_def,
if (roll < pit_list[i].rare)
{
mons_place(
- mgen_data::sleeper_at(
- pit_list[i].type,
- coord_def(x, y)));
+ mgen_data::sleeper_at( pit_list[i].type,
+ coord_def(x, y)));
break;
}
}
@@ -3336,9 +3335,8 @@ static void _special_room(int level_number, spec_room &sr)
continue;
mons_place(
- mgen_data::sleeper_at(
- mons_alloc[random2(10)],
- coord_def(x, y)));
+ mgen_data::sleeper_at( mons_alloc[random2(10)],
+ coord_def(x, y) ));
}
break;
@@ -3372,9 +3370,8 @@ static void _special_room(int level_number, spec_room &sr)
continue;
mons_place(
- mgen_data::sleeper_at(
- mons_alloc[random2(10)],
- coord_def(x, y) ));
+ mgen_data::sleeper_at( mons_alloc[random2(10)],
+ coord_def(x, y) ));
}
// put the boss monster down
@@ -3384,7 +3381,7 @@ static void _special_room(int level_number, spec_room &sr)
break;
case SROOM_TREASURY:
- // should only appear in deep levels, with a guardian
+ // Should only appear in deep levels, with a guardian
// Maybe have several types of treasure room?
// place treasure {dlb}:
for (x = sr.x1; x <= sr.x2; x++)
@@ -3417,7 +3414,8 @@ static void _special_room(int level_number, spec_room &sr)
mgen_data::sleeper_at(
MONS_GUARDIAN_NAGA,
coord_def(sr.x1 + random2( sr.x2 - sr.x1 ),
- sr.y1 + random2( sr.y2 - sr.y1 )) ));
+ sr.y1 + random2( sr.y2 - sr.y1 )),
+ MG_PATROLLING ));
break;
case SROOM_BEEHIVE:
@@ -3472,15 +3470,16 @@ static void _beehive(spec_room &sr)
mons_place(
mgen_data::sleeper_at(
- one_chance_in(7) ?
- MONS_KILLER_BEE_LARVA : MONS_KILLER_BEE,
+ one_chance_in(7) ? MONS_KILLER_BEE_LARVA
+ : MONS_KILLER_BEE,
coord_def(x, y)));
}
mons_place(
mgen_data::sleeper_at(
MONS_QUEEN_BEE,
- coord_def(queenx, queeny )));
+ coord_def(queenx, queeny ),
+ MG_PATROLLING));
}
// used for placement of vaults
@@ -4517,7 +4516,7 @@ bool dgn_place_monster(mons_spec &mspec,
if (mspec.mid != -1)
{
const int mid = mspec.mid;
- const bool m_generate_awake = generate_awake || mspec.generate_awake;
+ const bool m_generate_awake = (generate_awake || mspec.generate_awake);
const int mlev = mspec.mlevel;
if (mlev)
@@ -4541,12 +4540,12 @@ bool dgn_place_monster(mons_spec &mspec,
}
mgen_data mg(static_cast<monster_type>(mid));
- mg.power = monster_level;
- mg.behaviour = m_generate_awake ? BEH_WANDER : BEH_SLEEP;
+ mg.power = monster_level;
+ mg.behaviour = (m_generate_awake ? BEH_WANDER : BEH_SLEEP);
mg.base_type = mspec.monbase;
- mg.number = mspec.number;
- mg.colour = mspec.colour;
- mg.pos = coord_def(vx, vy);
+ mg.number = mspec.number;
+ mg.colour = mspec.colour;
+ mg.pos = coord_def(vx, vy);
const int mindex = place_monster(mg);
if (mindex != -1)
@@ -4878,8 +4877,8 @@ static int _vault_grid( vault_placement &place,
if (vgrid == 'S' || vgrid == 'H')
{
- const monster_type mtype =
- (vgrid == 'H') ? MONS_ORANGE_STATUE : MONS_SILVER_STATUE;
+ const monster_type mtype = ((vgrid == 'H') ? MONS_ORANGE_STATUE
+ : MONS_SILVER_STATUE);
grd[vx][vy] = DNGN_FLOOR;
@@ -6231,10 +6230,7 @@ static void _labyrinth_place_items(const coord_def &end)
static void _labyrinth_place_exit(const coord_def &end)
{
_labyrinth_place_items(end);
- mons_place(
- mgen_data::sleeper_at(
- MONS_MINOTAUR,
- end));
+ mons_place( mgen_data::sleeper_at(MONS_MINOTAUR, end, MG_PATROLLING) );
grd(end) = DNGN_ESCAPE_HATCH_UP;
}
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index 9a49d673fa..e8ef2d8705 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -1902,14 +1902,33 @@ bool recharge_wand(int item_slot)
} // end recharge_wand()
// Sets foe target of friendly monsters.
-static void set_friendly_foes()
+// If allow_patrol is true, patrolling monsters get MHITNOT instead.
+static void _set_friendly_foes(bool allow_patrol = false)
{
for (int i = 0; i < MAX_MONSTERS; ++i)
{
monsters *mon(&menv[i]);
if (!mon->alive() || !mons_near(mon) || !mons_friendly(mon))
continue;
- mon->foe = you.pet_target;
+
+ mon->foe = (allow_patrol && mon->is_patrolling() ? MHITNOT
+ : you.pet_target);
+ }
+}
+
+static void _set_allies_patrol_point(bool clear = false)
+{
+ for (int i = 0; i < MAX_MONSTERS; ++i)
+ {
+ monsters *mon(&menv[i]);
+ if (!mon->alive() || !mons_near(mon) || !mons_friendly(mon))
+ continue;
+
+ mon->patrol_point = (clear ? coord_def(0, 0)
+ : coord_def(mon->x, mon->y));
+
+ if (!clear)
+ mon->behaviour = BEH_WANDER;
}
}
@@ -1986,7 +2005,9 @@ void yell(bool force)
}
}
- mpr(" h - Order allies to stop attacking");
+ mpr(" s - Order allies to stop attacking");
+ mpr(" w - Order allies to wait here");
+ mpr(" f - Order allies to follow you");
}
mprf(" Anything else - Stay silent%s",
@@ -1996,13 +2017,47 @@ void yell(bool force)
switch (keyn)
{
- case '!': // for players using the old keyset
+ case '!': // for players using the old keyset
case 't':
mprf(MSGCH_SOUND, "You %s for attention!", shout_verb.c_str());
you.turn_is_over = true;
noisy( noise_level, you.x_pos, you.y_pos );
return;
+ case 'f':
+ case 's':
+ mons_targd = MHITYOU;
+ if (keyn == 'f')
+ {
+ // Don't reset patrol points for 'Stop fighting!'
+ _set_allies_patrol_point(true);
+ mpr("Follow me!");
+ }
+ else
+ mpr("Stop fighting!");
+ break;
+
+ case 'w':
+ mpr("Wait here!");
+ mons_targd = MHITNOT;
+ _set_allies_patrol_point();
+ break;
+
+ case 'p':
+ if (you.duration[DUR_BERSERKER])
+ {
+ canned_msg(MSG_TOO_BERSERK);
+ return;
+ }
+
+ if (targ_prev)
+ {
+ mons_targd = you.prev_targ;
+ mpr("Attack!");
+ break;
+ }
+
+ // fall through
case 'a':
if (you.duration[DUR_BERSERKER])
{
@@ -2027,39 +2082,22 @@ void yell(bool force)
}
mons_targd = mgrd[targ.tx][targ.ty];
+ mpr("Attack!");
break;
- case 'p':
- if (you.duration[DUR_BERSERKER])
- {
- canned_msg(MSG_TOO_BERSERK);
- return;
- }
-
- if (targ_prev)
- {
- mons_targd = you.prev_targ;
- break;
- }
-
- case 'h':
- mons_targd = MHITYOU;
- break;
-
- // fall through...
default:
mpr("Okely-dokely.");
return;
}
- if (mons_targd != MHITNOT)
- {
- you.pet_target = mons_targd;
- set_friendly_foes();
- }
+ you.pet_target = mons_targd;
+ // Allow patrolling for "Stop fighting!" and "Wait here!"
+ _set_friendly_foes(keyn == 's' || keyn == 'w');
+
+ if (mons_targd != MHITNOT && mons_targd != MHITYOU)
+ mpr("Attack!");
noisy( 10, you.x_pos, you.y_pos );
- mpr(mons_targd == MHITYOU ? "Come here!" : "Attack!");
} // end yell()
bool forget_inventory(bool quiet)
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index 5c70f5e1f5..ed86c042b7 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -1028,6 +1028,7 @@ public:
unsigned char y;
unsigned char target_x;
unsigned char target_y;
+ coord_def patrol_point;
FixedVector<short, NUM_MONSTER_SLOTS> inv;
monster_spells spells;
mon_attitude_type attitude;
@@ -1104,6 +1105,7 @@ public:
void timeout_enchantments(int levels);
+ bool is_patrolling() const;
bool needs_transit() const;
void set_transit(const level_id &destination);
bool find_place_to_live(bool near_player = false);
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index 9fea017008..8a26e078f8 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -357,13 +357,12 @@ melee_attack::melee_attack(actor *attk, actor *defn,
void melee_attack::check_hand_half_bonus_eligible()
{
- hand_half_bonus =
- unarmed_ok
- && !can_do_unarmed
- && !shield
- && weapon
- && !item_cursed( *weapon )
- && hands == HANDS_HALF;
+ hand_half_bonus = (unarmed_ok
+ && !can_do_unarmed
+ && !shield
+ && weapon
+ && !item_cursed( *weapon )
+ && hands == HANDS_HALF);
}
void melee_attack::init_attack()
@@ -396,11 +395,11 @@ void melee_attack::init_attack()
water_attack = is_water_attack(attacker, defender);
attacker_visible = attacker->visible();
- attacker_invisible = !attacker_visible && see_grid(attacker->pos());
- defender_visible = defender && defender->visible();
- defender_invisible = !defender_visible && defender
- && see_grid(defender->pos());
- needs_message = attacker_visible || defender_visible;
+ attacker_invisible = (!attacker_visible && see_grid(attacker->pos()));
+ defender_visible = (defender && defender->visible());
+ defender_invisible = (!defender_visible && defender
+ && see_grid(defender->pos()));
+ needs_message = (attacker_visible || defender_visible);
if (defender && defender->submerged())
unarmed_ok = false;
@@ -607,10 +606,13 @@ bool melee_attack::attack()
if (attacker->atype() == ACT_PLAYER)
{
- if (!stop_attack_prompt(def, false, false))
- set_attack_conducts(def, conduct);
- else
+ if (stop_attack_prompt(def, false, false))
+ {
cancel_attack = true;
+ return (false);
+ }
+ else
+ set_attack_conducts(def, conduct);
}
// Trying to stay general beyond this point is a recipe for insanity.
@@ -782,8 +784,8 @@ bool melee_attack::player_attack()
if (damage_done > 0)
{
- int blood =
- _modify_blood_amount(damage_done, attacker->damage_type());
+ int blood = _modify_blood_amount(damage_done,
+ attacker->damage_type());
if (blood > defender->stat_hp())
blood = defender->stat_hp();
@@ -3080,8 +3082,8 @@ bool melee_attack::mons_attack_mons()
behaviour_event(def, ME_WHACK, monster_index(atk));
}
- // if an enemy attacked a friend, set the pet target if it isn't
- // set already
+ // If an enemy attacked a friend, set the pet target if it isn't
+ // set already.
if (perceived_attack && atk->alive() && mons_friendly(def)
&& !mons_wont_attack(atk) && you.pet_target == MHITNOT)
{
@@ -3837,8 +3839,8 @@ void melee_attack::mons_perform_attack_rounds()
if (defender->atype() == ACT_MONSTER)
type = defender->id();
- int blood
- = _modify_blood_amount(damage_done, attacker->damage_type());
+ int blood = _modify_blood_amount(damage_done,
+ attacker->damage_type());
if (blood > defender->stat_hp())
blood = defender->stat_hp();
@@ -3983,12 +3985,20 @@ bool you_attack(int monster_attacked, bool unarmed_attacks)
wielded_weapon_check(attk.weapon);
bool attack = attk.attack();
- if (attack && (is_sanctuary(you.x_pos, you.y_pos)
- || is_sanctuary(defender->x, defender->y)))
+ if (!attack)
+ {
+ // Attack was cancelled or unsuccessful...
+ if (attk.cancel_attack)
+ you.turn_is_over = false;
+ return (false);
+ }
+
+ if (is_sanctuary(you.x_pos, you.y_pos)
+ || is_sanctuary(defender->x, defender->y))
{
remove_sanctuary(true);
}
- return attack;
+ return (true);
}
// Lose attack energy for attacking with a weapon. The monster has already lost
@@ -4026,7 +4036,7 @@ bool monster_attack(int monster_attacking)
// Friendly and good neutral monsters won't attack unless confused.
if (mons_wont_attack(attacker) && !mons_is_confused(attacker))
- return false;
+ return (false);
// In case the monster hasn't noticed you, bumping into it will
// change that.
@@ -4034,7 +4044,7 @@ bool monster_attack(int monster_attacking)
melee_attack attk(attacker, &you);
attk.attack();
- return true;
+ return (true);
} // end monster_attack()
bool monsters_fight(int monster_attacking, int monster_attacked)
diff --git a/crawl-ref/source/it_use3.cc b/crawl-ref/source/it_use3.cc
index f478c5ee9d..4c8478290c 100644
--- a/crawl-ref/source/it_use3.cc
+++ b/crawl-ref/source/it_use3.cc
@@ -296,16 +296,16 @@ static bool reaching_weapon_attack(const item_def& wpn)
* slips between the squares.
*/
- // if we're attacking more than a space away
- if ((x_distance > 1) || (y_distance > 1))
+ // If we're attacking more than a space away...
+ if (x_distance > 1 || y_distance > 1)
{
const int x_middle = MAX(beam.tx, you.x_pos) - (x_distance / 2);
const int y_middle = MAX(beam.ty, you.y_pos) - (y_distance / 2);
- // if either the x or the y is the same, we should check for
+ // If either the x or the y is the same, we should check for
// a monster:
- if (((beam.tx == you.x_pos) || (beam.ty == you.y_pos))
- && (mgrd[x_middle][y_middle] != NON_MONSTER))
+ if ((beam.tx == you.x_pos || beam.ty == you.y_pos)
+ && mgrd[x_middle][y_middle] != NON_MONSTER)
{
const int skill = weapon_skill( wpn.base_type, wpn.sub_type );
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 5197d2ac2f..edd906743f 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -2214,7 +2214,7 @@ bool ms_waste_of_time( const monsters *mon, spell_type monspell )
// life-protected if he has triple life protection.
const mon_holy_type holiness = foe->holiness();
return (holiness == MH_UNDEAD || holiness == MH_DEMONIC
- || holiness == MH_NONLIVING || holiness == MH_PLANT);
+ || holiness == MH_NONLIVING || holiness == MH_PLANT);
}
case SPELL_DISPEL_UNDEAD:
@@ -2222,7 +2222,7 @@ bool ms_waste_of_time( const monsters *mon, spell_type monspell )
case SPELL_BACKLIGHT:
{
- ret = !foe || foe->backlit();
+ ret = (!foe || foe->backlit());
break;
}
@@ -2591,10 +2591,11 @@ bool monster_senior(const monsters *m1, const monsters *m2)
monsters::monsters()
: type(-1), hit_points(0), max_hit_points(0), hit_dice(0),
ac(0), ev(0), speed(0), speed_increment(0), x(0), y(0),
- target_x(0), target_y(0), inv(NON_ITEM), spells(), attitude(ATT_HOSTILE),
- behaviour(BEH_WANDER), foe(MHITYOU), enchantments(), flags(0L),
- experience(0), number(0), colour(BLACK), foe_memory(0), shield_blocks(0),
- god(GOD_NO_GOD), ghost(), seen_context("")
+ target_x(0), target_y(0), patrol_point(0, 0), inv(NON_ITEM), spells(),
+ attitude(ATT_HOSTILE), behaviour(BEH_WANDER), foe(MHITYOU),
+ enchantments(), flags(0L), experience(0), number(0), colour(BLACK),
+ foe_memory(0), shield_blocks(0), god(GOD_NO_GOD), ghost(),
+ seen_context("")
{
}
@@ -2641,6 +2642,7 @@ void monsters::reset()
mgrd[x][y] = NON_MONSTER;
x = y = 0;
+ patrol_point = coord_def(0, 0);
ghost.reset(NULL);
}
@@ -2660,6 +2662,7 @@ void monsters::init_with(const monsters &mon)
y = mon.y;
target_x = mon.target_x;
target_y = mon.target_y;
+ patrol_point = mon.patrol_point;
inv = mon.inv;
spells = mon.spells;
attitude = mon.attitude;
@@ -4558,12 +4561,17 @@ void monsters::destroy_inventory()
}
}
+bool monsters::is_patrolling() const
+{
+ return (patrol_point != coord_def(0, 0));
+}
+
bool monsters::needs_transit() const
{
return ((mons_is_unique(type)
- || (flags & MF_BANISHED)
- || type == MONS_ROYAL_JELLY
- || (you.level_type == LEVEL_DUNGEON && hit_dice > 8 + random2(25)))
+ || (flags & MF_BANISHED)
+ || type == MONS_ROYAL_JELLY
+ || you.level_type == LEVEL_DUNGEON && hit_dice > 8 + random2(25))
&& !mons_is_summoned(this));
}
@@ -4613,7 +4621,7 @@ void monsters::load_spells(mon_spellbook_type book)
bool monsters::has_hydra_multi_attack() const
{
return (type == MONS_HYDRA
- || (mons_is_zombified(this) && base_monster == MONS_HYDRA));
+ || mons_is_zombified(this) && base_monster == MONS_HYDRA);
}
bool monsters::has_ench(enchant_type ench) const
@@ -4728,9 +4736,9 @@ void monsters::add_enchantment_effect(const mon_enchant &ench, bool quiet)
case ENCH_CHARM:
behaviour = BEH_SEEK;
- target_x = you.x_pos;
- target_y = you.y_pos;
- foe = MHITYOU;
+ target_x = you.x_pos;
+ target_y = you.y_pos;
+ foe = MHITYOU;
break;
default:
@@ -5572,7 +5580,8 @@ actor *monsters::get_foe() const
int monsters::foe_distance() const
{
const actor *afoe = get_foe();
- return (afoe? pos().distance_from(afoe->pos()) : INFINITE_DISTANCE);
+ return (afoe ? pos().distance_from(afoe->pos())
+ : INFINITE_DISTANCE);
}
bool monsters::can_go_berserk() const
diff --git a/crawl-ref/source/monplace.h b/crawl-ref/source/monplace.h
index 6a72bca628..05be654743 100644
--- a/crawl-ref/source/monplace.h
+++ b/crawl-ref/source/monplace.h
@@ -94,10 +94,11 @@ enum proximity_type // proximity to player to create monster
enum mgen_flag_type
{
- MG_PERMIT_BANDS = 0x1,
- MG_FORCE_PLACE = 0x2,
- MG_FORCE_BEH = 0x4,
- MG_PLAYER_MADE = 0x8
+ MG_PERMIT_BANDS = 0x01,
+ MG_FORCE_PLACE = 0x02,
+ MG_FORCE_BEH = 0x04,
+ MG_PLAYER_MADE = 0x08,
+ MG_PATROLLING = 0x10
};
// A structure with all the data needed to whip up a new monster.
@@ -186,6 +187,7 @@ struct mgen_data
bool permit_bands() const { return (flags & MG_PERMIT_BANDS); }
bool force_place() const { return (flags & MG_FORCE_PLACE); }
+ bool needs_patrol_point() const { return (flags & MG_PATROLLING); }
// Is there a valid position set on this struct that we want to use
// when placing the monster?
@@ -194,7 +196,8 @@ struct mgen_data
bool summoned() const { return (abjuration_duration > 0); }
static mgen_data sleeper_at(monster_type what,
- const coord_def &where)
+ const coord_def &where,
+ unsigned flags = 0)
{
return mgen_data(what, BEH_SLEEP, 0, where);
}
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 5d782c3120..ab4a48d441 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -732,7 +732,7 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent)
if (mons_is_caught(monster))
mons_clear_trapping_net(monster);
- // update list of monsters beholding player
+ // Update list of monsters beholding player.
update_beholders(monster, true);
const int monster_killed = monster_index(monster);
@@ -1029,7 +1029,7 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent)
MDAM_DEAD);
}
- // no piety loss if god gifts killed by other monsters
+ // No piety loss if god gifts killed by other monsters.
if (mons_friendly(monster) && !testbits(monster->flags,MF_GOD_GIFT))
{
did_god_conduct(DID_FRIEND_DIED, 1 + (monster->hit_dice / 2),
@@ -1319,7 +1319,7 @@ void monster_cleanup(monsters *monster)
if (you.pet_target == monster_killed)
you.pet_target = MHITNOT;
-} // end monster_cleanup()
+}
static bool _jelly_divide(monsters * parent)
{
@@ -1330,10 +1330,10 @@ static bool _jelly_divide(monsters * parent)
if (!mons_class_flag( parent->type, M_SPLITS ) || parent->hit_points == 1)
return (false);
- // first, find a suitable spot for the child {dlb}:
+ // First, find a suitable spot for the child {dlb}:
for (jex = -1; jex < 3; jex++)
{
- // loop moves beyond those tiles contiguous to parent {dlb}:
+ // Loop moves beyond those tiles contiguous to parent {dlb}:
if (jex > 1)
return (false);
@@ -1351,7 +1351,7 @@ static bool _jelly_divide(monsters * parent)
if (foundSpot)
break;
- } // end of for jex
+ } // end of for jex
int k = 0; // must remain outside loop that follows {dlb}
@@ -1366,7 +1366,7 @@ static bool _jelly_divide(monsters * parent)
return (false);
}
- // handle impact of split on parent {dlb}:
+ // Handle impact of split on parent {dlb}:
parent->max_hit_points /= 2;
if (parent->hit_points > parent->max_hit_points)
@@ -1375,11 +1375,11 @@ static bool _jelly_divide(monsters * parent)
parent->init_experience();
parent->experience = parent->experience * 3 / 5 + 1;
- // create child {dlb}:
- // this is terribly partial and really requires
+ // Create child {dlb}:
+ // This is terribly partial and really requires
// more thought as to generation ... {dlb}
*child = *parent;
- child->max_hit_points = child->hit_points;
+ child->max_hit_points = child->hit_points;
child->speed_increment = 70 + random2(5);
child->x = parent->x + jex;
child->y = parent->y + jey;
@@ -1395,7 +1395,7 @@ static bool _jelly_divide(monsters * parent)
return (true);
} // end jelly_divide()
-// if you're invis and throw/zap whatever, alerts menv to your position
+// If you're invis and throw/zap whatever, alerts menv to your position.
void alert_nearby_monsters(void)
{
monsters *monster = 0; // NULL {dlb}
@@ -2028,6 +2028,9 @@ void behaviour_event( monsters *mon, int event, int src,
// interesting for a moment. -- bwr
if (!isSmart || mon->foe == MHITNOT || mon->behaviour == BEH_WANDER)
{
+ if (mon->is_patrolling())
+ break;
+
mon->target_x = src_x;
mon->target_y = src_y;
}
@@ -2035,13 +2038,13 @@ void behaviour_event( monsters *mon, int event, int src,
case ME_WHACK:
case ME_ANNOY:
- // will turn monster against <src>, unless they
+ // Will turn monster against <src>, unless they
// are BOTH friendly or good neutral AND stupid,
// or else fleeing anyway. Hitting someone over
// the head, of course, always triggers this code.
if (event == ME_WHACK
|| ((wontAttack != sourceWontAttack || isSmart)
- && mon->behaviour != BEH_FLEE && mon->behaviour != BEH_PANIC))
+ && mon->behaviour != BEH_FLEE && mon->behaviour != BEH_PANIC))
{
// (plain) plants and fungi cannot flee or fight back
if (mon->type == MONS_FUNGUS || mon->type == MONS_PLANT)
@@ -2059,16 +2062,19 @@ void behaviour_event( monsters *mon, int event, int src,
}
}
- // now set target x,y so that monster can whack
- // back (once) at an invisible foe
+ // Now set target x,y so that monster can whack
+ // back (once) at an invisible foe.
if (event == ME_WHACK)
setTarget = true;
break;
case ME_ALERT:
- // will alert monster to <src> and turn them
+ if (mon->is_patrolling())
+ break;
+
+ // Will alert monster to <src> and turn them
// against them, unless they have a current foe.
- // it won't turn friends hostile either.
+ // It won't turn friends hostile either.
if (mon->behaviour != BEH_CORNERED && mon->behaviour != BEH_PANIC
&& mon->behaviour != BEH_FLEE)
{
@@ -2108,7 +2114,7 @@ void behaviour_event( monsters *mon, int event, int src,
break;
}
- // Just set behaviour.. foe doesn't change.
+ // Just set behaviour... foe doesn't change.
if (mon->behaviour != BEH_CORNERED)
simple_monster_message(mon, " turns to fight!");
@@ -2135,14 +2141,45 @@ void behaviour_event( monsters *mon, int event, int src,
}
}
- // now, break charms if appropriate
+ // Now, break charms if appropriate.
if (breakCharm)
mon->del_ench(ENCH_CHARM);
- // do any resultant foe or state changes
+ // Do any resultant foe or state changes.
_handle_behaviour( mon );
}
+static bool _choose_random_target_grid_in_los(monsters *mon)
+{
+ int pos_x, pos_y;
+ int count_grids = 0;
+ for (int j = -LOS_RADIUS; j < LOS_RADIUS; j++)
+ for (int k = -LOS_RADIUS; k < LOS_RADIUS; k++)
+ {
+ if (j == 0 && k == 0)
+ continue;
+
+ pos_x = mon->x + j;
+ pos_y = mon->y + k;
+
+ if (!in_bounds(pos_x, pos_y))
+ continue;
+ if (!mon->mon_see_grid(pos_x, pos_y))
+ continue;
+ if (!mon->can_pass_through_feat(grd[pos_x][pos_y]))
+ continue;
+
+ if (one_chance_in(++count_grids))
+ {
+ mon->target_x = pos_x;
+ mon->target_y = pos_y;
+ }
+ }
+
+ return (count_grids);
+}
+
+
//---------------------------------------------------------------
//
// handle_behaviour
@@ -2164,11 +2201,12 @@ static void _handle_behaviour(monsters *mon)
bool wontAttack = mons_wont_attack(mon);
bool proxPlayer = mons_near(mon);
bool proxFoe;
- bool isHurt = (mon->hit_points <= mon->max_hit_points / 4 - 1);
- bool isHealthy = (mon->hit_points > mon->max_hit_points / 2);
- bool isSmart = (mons_intel(mon->type) > I_ANIMAL);
- bool isScared = mon->has_ench(ENCH_FEAR);
- bool isMobile = !mons_is_stationary(mon);
+ bool isHurt = (mon->hit_points <= mon->max_hit_points / 4 - 1);
+ bool isHealthy = (mon->hit_points > mon->max_hit_points / 2);
+ bool isSmart = (mons_intel(mon->type) > I_ANIMAL);
+ bool isScared = mon->has_ench(ENCH_FEAR);
+ bool isMobile = !mons_is_stationary(mon);
+ bool patrolling = mon->is_patrolling();
// check for confusion -- early out.
if (mon->has_ench(ENCH_CONFUSION))
@@ -2178,7 +2216,7 @@ static void _handle_behaviour(monsters *mon)
return;
}
- // validate current target exists
+ // Validate current target exists.
if (mon->foe != MHITNOT && mon->foe != MHITYOU
&& menv[mon->foe].type == -1)
{
@@ -2197,15 +2235,15 @@ static void _handle_behaviour(monsters *mon)
proxPlayer = false;
const int intel = mons_intel(mon->type);
- // now, the corollary to that is that sometimes, if a
- // player is right next to a monster, they will 'see'
+ // Now, the corollary to that is that sometimes, if a
+ // player is right next to a monster, they will 'see'.
if (grid_distance( you.x_pos, you.y_pos, mon->x, mon->y ) == 1
&& one_chance_in(3))
{
proxPlayer = true;
}
- // [dshaligram] Very smart monsters have a chance of cluing in to
+ // [dshaligram] Very smart monsters have a chance of clueing in to
// invisible players in various ways.
else if (intel == I_NORMAL && one_chance_in(13)
|| intel == I_HIGH && one_chance_in(6))
@@ -2239,7 +2277,7 @@ static void _handle_behaviour(monsters *mon)
if (isNeutral && mon->foe == MHITNOT)
_set_nearest_monster_foe(mon);
- // monsters do not attack themselves {dlb}
+ // Monsters do not attack themselves. {dlb}
if (mon->foe == monster_index(mon))
mon->foe = MHITNOT;
@@ -2280,7 +2318,7 @@ static void _handle_behaviour(monsters *mon)
int foe_x = you.x_pos;
int foe_y = you.y_pos;
- // evaluate these each time; they may change
+ // Evaluate these each time; they may change.
if (mon->foe == MHITNOT)
proxFoe = false;
else
@@ -2303,12 +2341,12 @@ static void _handle_behaviour(monsters *mon)
}
}
- // track changes to state; attitude never changes here.
- beh_type new_beh = mon->behaviour;
+ // Track changes to state; attitude never changes here.
+ beh_type new_beh = mon->behaviour;
unsigned int new_foe = mon->foe;
// take care of monster state changes
- switch(mon->behaviour)
+ switch (mon->behaviour)
{
case BEH_SLEEP:
// default sleep state
@@ -2321,7 +2359,7 @@ static void _handle_behaviour(monsters *mon)
// no foe? then wander or seek the player
if (mon->foe == MHITNOT)
{
- if (!proxPlayer || isNeutral)
+ if (!proxPlayer || isNeutral || patrolling)
new_beh = BEH_WANDER;
else
{
@@ -2329,13 +2367,19 @@ static void _handle_behaviour(monsters *mon)
mon->target_x = you.x_pos;
mon->target_y = you.y_pos;
}
-
break;
}
// foe gone out of LOS?
if (!proxFoe)
{
+ if (patrolling)
+ {
+ new_foe = MHITNOT;
+ new_beh = BEH_WANDER;
+ break;
+ }
+
if (isFriendly)
{
new_foe = MHITYOU;
@@ -2410,11 +2454,11 @@ static void _handle_behaviour(monsters *mon)
break; // switch/case BEH_SEEK
}
- // monster can see foe: continue 'tracking'
- // by updating target x,y
+ // Monster can see foe: continue 'tracking'
+ // by updating target x,y.
if (mon->foe == MHITYOU)
{
- // sometimes, your friends will wander a bit.
+ // Sometimes, your friends will wander a bit.
if (isFriendly && one_chance_in(8))
{
mon->target_x = 10 + random2(GXM - 10);
@@ -2439,7 +2483,7 @@ static void _handle_behaviour(monsters *mon)
break;
case BEH_WANDER:
- // is our foe in LOS?
+ // Is our foe in LOS?
// Batty monsters don't automatically reseek so that
// they'll flitter away, we'll reset them just before
// they get movement in handle_monsters() instead. -- bwr
@@ -2462,14 +2506,25 @@ static void _handle_behaviour(monsters *mon)
|| one_chance_in(20)
|| testbits( mon->flags, MF_BATTY ))
{
- mon->target_x = 10 + random2(GXM - 10);
- mon->target_y = 10 + random2(GYM - 10);
+ if (patrolling)
+ {
+ if (mon->patrol_point != coord_def(mon->x, mon->y)
+ || !_choose_random_target_grid_in_los(mon))
+ {
+ mon->target_x = mon->patrol_point.x;
+ mon->target_y = mon->patrol_point.y;
+ }
+ }
+ else
+ {
+ mon->target_x = 10 + random2(GXM - 10);
+ mon->target_y = 10 + random2(GYM - 10);
+ }
}
- // during their wanderings, monsters will
- // eventually relax their guard (stupid
- // ones will do so faster, smart monsters
- // have longer memories
+ // During their wanderings, monsters will eventually relax their
+ // guard (stupid ones will do so faster, smart monsters have
+ // longer memories.
if (!proxFoe && mon->foe != MHITNOT
&& one_chance_in( isSmart ? 60 : 20 ))
{
@@ -2482,11 +2537,9 @@ static void _handle_behaviour(monsters *mon)
if (isHealthy && !isScared)
new_beh = BEH_SEEK;
- // Smart monsters flee until they can
- // flee no more... possible to get a
- // 'CORNERED' event, at which point
- // we can jump back to WANDER if the foe
- // isn't present.
+ // Smart monsters flee until they can flee no more...
+ // possible to get a 'CORNERED' event, at which point
+ // we can jump back to WANDER if the foe isn't present.
// XXX: If a monster can move through solid grids, then it
// should preferentially flee towards the nearest solid grid
@@ -2497,13 +2550,13 @@ static void _handle_behaviour(monsters *mon)
if (isFriendly)
{
- // Special-cased below so that it will flee *towards* you
+ // Special-cased below so that it will flee *towards* you.
mon->target_x = you.x_pos;
mon->target_y = you.y_pos;
}
else if (proxFoe)
{
- // try to flee _from_ the correct position
+ // Try to flee _from_ the correct position.
mon->target_x = foe_x;
mon->target_y = foe_y;
}
@@ -2516,7 +2569,7 @@ static void _handle_behaviour(monsters *mon)
// foe gone out of LOS?
if (!proxFoe)
{
- if ((isFriendly || proxPlayer) && !isNeutral)
+ if ((isFriendly || proxPlayer) && !isNeutral && !patrolling)
new_foe = MHITYOU;
else
new_beh = BEH_WANDER;
@@ -2571,8 +2624,7 @@ static bool _mons_check_set_foe(monsters *mon, int x, int y,
return (false);
}
-// choose nearest monster as a foe
-// (used for berserking monsters)
+// Choose nearest monster as a foe. (Used for berserking monsters.)
void _set_nearest_monster_foe(monsters *mon)
{
const bool friendly = mons_friendly(mon);
@@ -3579,7 +3631,7 @@ static bool _handle_reaching(monsters *monster)
{
int foe_x = menv[monster->foe].x;
int foe_y = menv[monster->foe].y;
- // same comments as to invisibility as above.
+ // Same comments as to invisibility as above.
if (monster->target_x == foe_x && monster->target_y == foe_y
&& monster->mon_see_grid(foe_x, foe_y, true))
{
@@ -4526,7 +4578,7 @@ static bool _handle_throw(monsters *monster, bolt & beem)
if (one_chance_in(archer? 9 : 5))
return (false);
- // don't allow offscreen throwing for now.
+ // Don't allow offscreen throwing for now.
if (monster->foe == MHITYOU && !mons_near(monster))
return (false);
@@ -4900,63 +4952,87 @@ static void _handle_monster_move(int i, monsters *monster)
}
else
{
- // calculates mmov_x, mmov_y based on monster target.
+ // Calculates mmov_x, mmov_y based on monster target.
_handle_movement(monster);
brkk = false;
- if (mons_is_confused(monster)
- || monster->type == MONS_AIR_ELEMENTAL
- && monster->has_ench(ENCH_SUBMERGED))
- {
- std::vector<coord_def> moves;
-
- int pfound = 0;
- for (int yi = -1; yi <= 1; ++yi)
- for (int xi = -1; xi <= 1; ++xi)
- {
- coord_def c = monster->pos() + coord_def(xi, yi);
- if (in_bounds(c) && monster->can_pass_through(c)
- && one_chance_in(++pfound))
- {
- mmov_x = xi;
- mmov_y = yi;
- }
- }
-
- if (random2(2 + pfound) < 2)
- mmov_x = mmov_y = 0;
-
- // Bounds check: don't let confused monsters try to run
- // off the map.
- if (monster->x + mmov_x < 0 || monster->x + mmov_x >= GXM)
- mmov_x = 0;
-
- if (monster->y + mmov_y < 0 || monster->y + mmov_y >= GYM)
- mmov_y = 0;
-
- if (!monster->can_pass_through(monster->x + mmov_x,
- monster->y + mmov_y))
- {
- mmov_x = mmov_y = 0;
- }
-
- if (mgrd[monster->x + mmov_x][monster->y + mmov_y] != NON_MONSTER
- && !is_sanctuary(monster->x, monster->y)
- && (mmov_x != 0 || mmov_y != 0))
- {
- monsters_fight(i,
- mgrd[monster->x + mmov_x][monster->y + mmov_y]);
-
- brkk = true;
- mmov_x = 0;
- mmov_y = 0;
- DEBUG_ENERGY_USE("monsters_fight()");
- }
- }
+ if (mons_is_confused(monster)
+ || monster->type == MONS_AIR_ELEMENTAL
+ && monster->has_ench(ENCH_SUBMERGED))
+ {
+ std::vector<coord_def> moves;
- if (brkk)
- continue;
+ int pfound = 0;
+ for (int yi = -1; yi <= 1; ++yi)
+ for (int xi = -1; xi <= 1; ++xi)
+ {
+ coord_def c = monster->pos() + coord_def(xi, yi);
+ if (in_bounds(c) && monster->can_pass_through(c)
+ && one_chance_in(++pfound))
+ {
+ mmov_x = xi;
+ mmov_y = yi;
+ }
+ }
+
+ if (random2(2 + pfound) < 2)
+ mmov_x = mmov_y = 0;
+
+ // Bounds check: don't let confused monsters try to run
+ // off the map.
+ if (monster->x + mmov_x < 0 || monster->x + mmov_x >= GXM)
+ mmov_x = 0;
+
+ if (monster->y + mmov_y < 0 || monster->y + mmov_y >= GYM)
+ mmov_y = 0;
+
+ if (!monster->can_pass_through(monster->x + mmov_x,
+ monster->y + mmov_y))
+ {
+ mmov_x = mmov_y = 0;
+ }
+
+ int enemy = mgrd[monster->x + mmov_x][monster->y + mmov_y];
+ if (enemy != NON_MONSTER
+ && !is_sanctuary(monster->x, monster->y)
+ && (mmov_x != 0 || mmov_y != 0))
+ {
+ if (monsters_fight(i, enemy))
+ {
+ brkk = true;
+ mmov_x = 0;
+ mmov_y = 0;
+ DEBUG_ENERGY_USE("monsters_fight()");
+ }
+ else
+ {
+ // FIXME: None of these work!
+ // Instead run away!
+ if (monster->add_ench(mon_enchant(ENCH_FEAR)))
+ {
+ behaviour_event(monster, ME_SCARE, MHITNOT,
+ monster->x + mmov_x,
+ monster->y + mmov_y);
+ }
+ break;
+/*
+ if (monster->foe == enemy || mons_friendly(monster)
+ && monster->foe == MHITYOU)
+ {
+ monster->foe = MHITNOT;
+ monster->behaviour = BEH_WANDER;
+ }
+
+ monster->target_x = 10 + random2(GXM - 10);
+ monster->target_y = 10 + random2(GYM - 10);
+*/
+ }
+ }
+ }
+
+ if (brkk)
+ continue;
}
_handle_nearby_ability( monster );
@@ -5027,7 +5103,7 @@ static void _handle_monster_move(int i, monsters *monster)
&& targmon != i
&& !mons_aligned(i, targmon))
{
- // figure out if they fight
+ // Figure out if they fight.
if (monsters_fight(i, targmon))
{
if (testbits(monster->flags, MF_BATTY))
@@ -5102,7 +5178,7 @@ static void _handle_monster_move(int i, monsters *monster)
}
update_beholders(monster);
- // reevaluate behaviour, since the monster's
+ // Reevaluate behaviour, since the monster's
// surroundings have changed (it may have moved,
// or died for that matter. Don't bother for
// dead monsters. :)
@@ -5883,13 +5959,13 @@ bool _mon_can_move_to_pos(const monsters *monster, const int count_x,
return false;
}
- // smacking the player is always a good move if we're
- // hostile (even if we're heading somewhere else)
- // also friendlies want to keep close to the player
- // so it's okay as well
+ // Smacking the player is always a good move if we're
+ // hostile (even if we're heading somewhere else).
+ // Also friendlies want to keep close to the player
+ // so it's okay as well.
- // smacking another monster is good, if the monsters
- // are aligned differently
+ // Smacking another monster is good, if the monsters
+ // are aligned differently.
if (mgrd[targ_x][targ_y] != NON_MONSTER)
{
if (just_check)
@@ -5902,6 +5978,7 @@ bool _mon_can_move_to_pos(const monsters *monster, const int count_x,
const int thismonster = monster_index(monster),
targmonster = mgrd[targ_x][targ_y];
+
if (mons_aligned(thismonster, targmonster)
&& targmonster != MHITNOT
&& targmonster != MHITYOU
diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc
index 076405a1ad..8610f3fd9c 100644
--- a/crawl-ref/source/tags.cc
+++ b/crawl-ref/source/tags.cc
@@ -1770,6 +1770,7 @@ static void marshall_monster(writer &th, const monsters &m)
marshallByte(th, m.y);
marshallByte(th, m.target_x);
marshallByte(th, m.target_y);
+ marshallCoord(th, m.patrol_point);
marshallLong(th, m.flags);
marshallLong(th, m.experience);
@@ -2030,6 +2031,10 @@ static void unmarshall_monster(reader &th, monsters &m)
m.y = unmarshallByte(th);
m.target_x = unmarshallByte(th);
m.target_y = unmarshallByte(th);
+
+ if (_tag_minor_version >= TAG_MINOR_MPATROL)
+ unmarshallCoord(th, m.patrol_point);
+
m.flags = unmarshallLong(th);
m.experience = static_cast<unsigned long>(unmarshallLong(th));
diff --git a/crawl-ref/source/tags.h b/crawl-ref/source/tags.h
index 5b30de1779..ae16aef150 100644
--- a/crawl-ref/source/tags.h
+++ b/crawl-ref/source/tags.h
@@ -56,7 +56,8 @@ enum tag_minor_version
TAG_MINOR_MONNAM = 5, // Monsters get individual names
TAG_MINOR_MONBASE = 6, // Zombie base monster gets its own field.
TAG_MINOR_FPICKUP = 7, // Added pickup option for allied monsters.
- TAG_MINOR_VERSION = 7 // Current version
+ TAG_MINOR_MPATROL = 8, // Added monster patrol points.
+ TAG_MINOR_VERSION = 8 // Current version
};
diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc
index e3b8ebe2f7..8be9d2ecc7 100644
--- a/crawl-ref/source/travel.cc
+++ b/crawl-ref/source/travel.cc
@@ -523,8 +523,8 @@ void init_travel_terrain_check(bool check_race_equip)
_set_pass_feature(DNGN_DEEP_WATER, water);
// Permanently levitating players can cross most hostile terrain.
- const signed char trav = you.permanent_levitation() ?
- TRAVERSABLE : IMPASSABLE;
+ const signed char trav = (you.permanent_levitation() ? TRAVERSABLE
+ : IMPASSABLE);
if (water != TRAVERSABLE)
_set_pass_feature(DNGN_DEEP_WATER, trav);