summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-11 11:12:18 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-11 11:12:18 +0000
commita9ac64d3b972306982b27c81632182864022f939 (patch)
tree137e07e43b0d975949d817e257645a0aa9053c23 /crawl-ref/source
parentdec341f5c123fcaab27d036d70dbef1456ad10fb (diff)
downloadcrawl-ref-a9ac64d3b972306982b27c81632182864022f939.tar.gz
crawl-ref-a9ac64d3b972306982b27c81632182864022f939.zip
Generalize swapping two pieces of terrain in function swap_terrain().
Encapsulate filling out a corpse object for a particular monster in fill_out_corpse(). Started work on "swap chaos weapon with weapon of victim" chaos effect, but am putting that off until there's some actor class virtual methods for changing inventory and equipment. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7807 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source')
-rw-r--r--crawl-ref/source/externs.h43
-rw-r--r--crawl-ref/source/fight.cc213
-rw-r--r--crawl-ref/source/items.cc5
-rw-r--r--crawl-ref/source/items.h3
-rw-r--r--crawl-ref/source/mon-util.cc137
-rw-r--r--crawl-ref/source/monstuff.cc66
-rw-r--r--crawl-ref/source/monstuff.h3
-rw-r--r--crawl-ref/source/overmap.cc24
-rw-r--r--crawl-ref/source/overmap.h1
-rw-r--r--crawl-ref/source/player.cc78
-rw-r--r--crawl-ref/source/spells3.cc13
-rw-r--r--crawl-ref/source/spells3.h2
-rw-r--r--crawl-ref/source/terrain.cc176
-rw-r--r--crawl-ref/source/terrain.h3
14 files changed, 601 insertions, 166 deletions
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index 44d6910904..f70b4376c7 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -236,6 +236,9 @@ public:
virtual bool alive() const = 0;
+ virtual bool is_summoned(int* duration = NULL,
+ int* summon_type = NULL) const = 0;
+
virtual void moveto(const coord_def &c) = 0;
virtual const coord_def& pos() const { return position; }
virtual coord_def& pos() { return position; }
@@ -253,6 +256,9 @@ public:
virtual bool can_pass_through(int x, int y) const;
virtual bool can_pass_through(const coord_def &c) const;
+ virtual bool is_habitable_feat(dungeon_feature_type actual_grid) const = 0;
+ bool is_habitable(const coord_def &pos) const;
+
virtual size_type body_size(int psize = PSIZE_TORSO,
bool base = false) const = 0;
virtual int body_weight() const = 0;
@@ -270,6 +276,20 @@ public:
}
virtual bool has_equipped(equipment_type eq, int sub_type) const;
+ bool can_wield(const item_def* item,
+ bool ignore_curse = false,
+ bool ignore_brand = false,
+ bool ignore_shield = false,
+ bool ignore_transform = false) const;
+ virtual bool can_wield(const item_def &item,
+ bool ignore_curse = false,
+ bool ignore_brand = false,
+ bool ignore_shield = false,
+ bool ignore_transform = false) const = 0;
+ virtual bool could_wield(const item_def &item,
+ bool ignore_brand = false,
+ bool ignore_transform = false) const = 0;
+
virtual int hunger_level() const { return HS_ENGORGED; }
virtual void make_hungry(int nutrition, bool silent = true)
{
@@ -968,12 +988,14 @@ public:
god_type deity() const;
bool alive() const;
-
+ bool is_summoned(int* duration = NULL, int* summon_type = NULL) const;
+
bool swimming() const;
bool submerged() const;
bool floundering() const;
bool extra_balanced() const;
bool can_pass_through_feat(dungeon_feature_type grid) const;
+ bool is_habitable_feat(dungeon_feature_type actual_grid) const;
size_type body_size(int psize = PSIZE_TORSO, bool base = false) const;
int body_weight() const;
int total_weight() const;
@@ -984,6 +1006,15 @@ public:
item_def *weapon(int which_attack = -1);
item_def *shield();
+ bool can_wield(const item_def &item,
+ bool ignore_curse = false,
+ bool ignore_brand = false,
+ bool ignore_shield = false,
+ bool ignore_transform = false) const;
+ bool could_wield(const item_def &item,
+ bool ignore_brand = false,
+ bool ignore_transform = false) const;
+
std::string name(description_level_type type,
bool force_visible = false) const;
std::string pronoun(pronoun_type pro, bool force_visible = false) const;
@@ -1314,6 +1345,7 @@ public:
bool floundering() const;
bool extra_balanced() const;
bool can_pass_through_feat(dungeon_feature_type grid) const;
+ bool is_habitable_feat(dungeon_feature_type actual_grid) const;
size_type body_size(int psize = PSIZE_TORSO, bool base = false) const;
int body_weight() const;
int total_weight() const;
@@ -1327,6 +1359,15 @@ public:
item_def *missiles();
item_def *shield();
+ bool can_wield(const item_def &item,
+ bool ignore_curse = false,
+ bool ignore_brand = false,
+ bool ignore_shield = false,
+ bool ignore_transform = false) const;
+ bool could_wield(const item_def &item,
+ bool ignore_brand = false,
+ bool ignore_transform = false) const;
+
int missile_count();
void wield_melee_weapon(int near = -1);
void swap_weapons(int near = -1);
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index 5722449471..b78b568ef3 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -25,7 +25,6 @@
#include "cloud.h"
#include "debug.h"
#include "delay.h"
-#include "dgnevent.h"
#include "effects.h"
#include "food.h"
#include "it_use2.h"
@@ -2425,6 +2424,20 @@ void melee_attack::chaos_affects_defender()
obvious_effect = false;
}
+static bool _ok_dest_grid(const coord_def &dest)
+{
+ dungeon_feature_type feat = grd(dest);
+
+ if (grid_destroys_items(feat) || grid_is_solid(feat)
+ || grid_is_water(feat) || grid_is_trap(feat, true)
+ || is_notable_terrain(feat))
+ {
+ return (false);
+ }
+
+ return (true);
+}
+
static bool _move_stairs(const actor* attacker, const actor* defender)
{
const coord_def orig_pos = attacker->pos();
@@ -2433,27 +2446,24 @@ static bool _move_stairs(const actor* attacker, const actor* defender)
if (grid_stair_direction(stair_feat) == CMD_NO_CMD)
return (false);
- // Moving shops is too much trouble, and anyways the player can't
- // use them to escape.
+ // The player can't use shops to escape, so don't bother.
if (stair_feat == DNGN_ENTER_SHOP)
return false;
- const bool stair_is_marker = env.markers.find(orig_pos, MAT_ANY);
+ // Don't move around notable terrain the player is aware of if it's
+ // out of sight.
+ if (is_notable_terrain(stair_feat)
+ && is_terrain_known(orig_pos.x, orig_pos.y) && !see_grid(orig_pos))
+ {
+ return (false);
+ }
coord_def dest(-1, -1);
// Prefer to send it under the defender.
- if (defender->alive() && defender->pos() != attacker->pos())
+ if (defender->alive() && defender->pos() != attacker->pos()
+ && _ok_dest_grid(defender->pos()))
{
- dungeon_feature_type feat = grd(defender->pos());
- if (!grid_destroys_items(feat) && !grid_is_solid(feat)
- && !grid_is_water(feat) && !grid_is_trap(feat, true))
- {
- dest = defender->pos();
- }
-
- // Don't try to swap two markers.
- if (stair_is_marker && env.markers.find(defender->pos(), MAT_ANY))
- dest.set(-1, -1);
+ dest = defender->pos();
}
if (!in_bounds(dest))
@@ -2463,13 +2473,7 @@ static bool _move_stairs(const actor* attacker, const actor* defender)
int squares = 0;
for (; ri; ++ri)
{
- // Don't try to swap two markers.
- if (stair_is_marker && env.markers.find(*ri, MAT_ANY))
- continue;
-
- dungeon_feature_type feat = grd(defender->pos());
- if (!grid_destroys_items(feat) && !grid_is_solid(feat)
- && !grid_is_water(feat) && !grid_is_trap(feat, true))
+ if (_ok_dest_grid(*ri))
{
if (one_chance_in(++squares))
dest = *ri;
@@ -2482,25 +2486,10 @@ static bool _move_stairs(const actor* attacker, const actor* defender)
ASSERT(dest != orig_pos);
- const bool dest_is_marker = env.markers.find(dest, MAT_ANY);
- const dungeon_feature_type dest_feat = grd(defender->pos());
-
- ASSERT(!(dest_is_marker && stair_is_marker));
-
- dungeon_terrain_changed(orig_pos, dest_feat);
- dungeon_terrain_changed(dest, stair_feat);
-
- if (stair_is_marker)
- {
- env.markers.move(orig_pos, dest);
- dungeon_events.move_listeners(orig_pos, dest);
- }
- else if (dest_is_marker)
- {
- env.markers.move(dest, orig_pos);
- dungeon_events.move_listeners(dest, orig_pos);
- }
+ if (!swap_features(orig_pos, dest))
+ return (false);
+ // Is player aware of it happening?
if (!see_grid(orig_pos) && !see_grid(dest))
return (true);
@@ -2612,8 +2601,9 @@ void melee_attack::chaos_affects_attacker()
return;
}
-static void _find_remains(monsters* mon, int &corpse_class, int &corpse,
- int &last_item, std::vector<int> items)
+static void _find_remains(monsters* mon, int &corpse_class, int &corpse_index,
+ item_def &fake_corpse, int &last_item,
+ std::vector<int> items)
{
for (int i = 0; i < NUM_MONSTER_SLOTS; i++)
{
@@ -2630,18 +2620,12 @@ static void _find_remains(monsters* mon, int &corpse_class, int &corpse,
items.push_back(idx);
}
- corpse = NON_ITEM;
- last_item = NON_ITEM;
-
- corpse_class = mons_species(mon->type);
+ corpse_index = NON_ITEM;
+ last_item = NON_ITEM;
- if (corpse_class == MONS_DRACONIAN)
- corpse_class = draco_subspecies(mon);
-
- if (mon->has_ench(ENCH_SHAPESHIFTER))
- corpse_class = MONS_SHAPESHIFTER;
- else if (mon->has_ench(ENCH_GLOWING_SHAPESHIFTER))
- corpse_class = MONS_GLOWING_SHAPESHIFTER;
+ corpse_class = fill_out_corpse(mon, fake_corpse, true);
+ if (corpse_class == -1 || mons_weight(corpse_class) == 0)
+ return;
// Stop at first non-matching corpse, since the freshest corpse will
// be at the top of the stack.
@@ -2649,29 +2633,21 @@ static void _find_remains(monsters* mon, int &corpse_class, int &corpse,
{
if (si->base_type == OBJ_CORPSES && si->sub_type == CORPSE_BODY)
{
- if (si->plus != corpse_class)
- break;
-
- // It should have just been dropped.
- if (corpse_freshness(*si) != FRESHEST_CORPSE)
- break;
-
- // If there was an opportunity to butcher it then it can't
- // have just been dropped.
- if (si->plus2 != 0)
- break;
-
- // It can't have been just made if it was picked up or
- // dropped.
- if (si->flags & (ISFLAG_DROPPED | ISFLAG_BEEN_IN_INV))
+ if (si->orig_monnum != fake_corpse.orig_monnum
+ || si->plus != fake_corpse.plus
+ || si->plus2 != fake_corpse.plus2
+ || si->special != fake_corpse.special
+ || si->flags != fake_corpse.flags)
+ {
break;
+ }
// If it's a hydra the number of heads must match.
if ((int) mon->number != si->props[MONSTER_NUMBER].get_long())
break;
// Got it!
- corpse = si.link();
+ corpse_index = si.link();
break;
}
else
@@ -2686,47 +2662,71 @@ static void _find_remains(monsters* mon, int &corpse_class, int &corpse,
}
}
-static bool _make_zombie(monsters* mon, int corpse_class, int corpse,
- int last_item)
+static bool _make_zombie(monsters* mon, int corpse_class, int corpse_index,
+ item_def &fake_corpse, int last_item)
{
// If the monster dropped a corpse then don't waste it by turning
// it into a zombie.
- if (corpse != NON_ITEM || !mons_class_can_be_zombified(corpse_class))
+ if (corpse_index != NON_ITEM || !mons_class_can_be_zombified(corpse_class))
return (false);
- int idx = get_item_slot();
+ // Good gods won't let their gifts/followers be raised as the undead.
+ if (is_good_god(mon->god))
+ return (false);
+
+ // First attempt to raise zombie fitted out with all its old equipment.
+ int zombie_index = -1;
+ int idx = get_item_slot(0);
if (idx != NON_ITEM && last_item != NON_ITEM)
{
- // Fake a corpse
- item_def &corpse_item(mitm[idx]);
- corpse_item.base_type = OBJ_CORPSES;
- corpse_item.sub_type = CORPSE_BODY;
- corpse_item.plus = corpse_class;
- corpse_item.orig_monnum = mon->type + 1;
- corpse_item.pos = mon->pos();
- corpse_item.quantity = 1;
- corpse_item.props[MONSTER_NUMBER] = short(mon->number);
-
- // Insert it in the item stack right after the monster's
- // last item, so it will be equipped with all the monster's
- // items.
- corpse_item.link = mitm[last_item].link;
+ mitm[idx] = fake_corpse;
+ mitm[idx].pos = mon->pos();
+
+ // Insert it in the item stack right after the monster's last item, so
+ // it will be equipped with all the monster's items.
+ mitm[idx].link = mitm[last_item].link;
mitm[last_item].link = idx;
- if (animate_remains(mon->pos(), CORPSE_BODY, mon->behaviour,
- mon->foe, mon->god, true, true))
- {
- if (you.can_see(mon))
- simple_monster_message(mon,
- " instantly turns into a zombie!");
- else if (see_grid(mon->pos()))
- mpr("A zombie appears out of nowhere!");
- return (true);
- }
+ void (animate_remains(mon->pos(), CORPSE_BODY, mon->behaviour,
+ mon->foe, mon->god, true, true, &zombie_index));
}
- return (false);
+
+ // No equipment to get, or couldn't get it for some reason.
+ if (zombie_index == -1)
+ {
+ monster_type type = (mons_zombie_size(mon->type) == Z_SMALL) ?
+ MONS_ZOMBIE_SMALL : MONS_ZOMBIE_LARGE;
+ zombie_index = create_monster(
+ mgen_data(type, mon->behaviour, 0, mon->pos(),
+ mon->foe, MG_FORCE_PLACE, mon->god,
+ (monster_type) mon->type, mon->number));
+ }
+
+ if (zombie_index == -1)
+ return (false);
+
+ monsters *zombie = &menv[zombie_index];
+
+ // Attempt to force zombie into exact same spot.
+ if (zombie->pos() != mon->pos() && zombie->is_habitable(mon->pos()))
+ zombie->move_to_pos(mon->pos());
+
+ if (you.can_see(mon))
+ {
+ if (you.can_see(zombie))
+ simple_monster_message(mon, " instantly turns into a zombie!");
+ else if (last_item != NON_ITEM)
+ simple_monster_message(mon, "'s equipment vanishes!");
+ }
+ else
+ simple_monster_message(zombie, " appears from thin air!");
+
+ return (true);
}
+// mon is a copy of the monster from before monster_die() was called,
+// though its hitpoints may be non-positive.
+//
// NOTE: Isn't called if monster dies from poisoning caused by chaos.
void melee_attack::chaos_killed_defender(monsters* mon)
{
@@ -2737,12 +2737,20 @@ void melee_attack::chaos_killed_defender(monsters* mon)
if (!attacker->alive())
return;
- int corpse_class, corpse, last_item;
+ if (attacker->atype() == ACT_PLAYER && you.banished)
+ return;
+
+ if (mon->is_summoned() || (mon->flags & (MF_BANISHED | MF_HARD_RESET)))
+ return;
+
+ int corpse_class, corpse_index, last_item;
+ item_def fake_corpse;
std::vector<int> items;
- _find_remains(mon, corpse_class, corpse, last_item, items);
+ _find_remains(mon, corpse_class, corpse_index, fake_corpse, last_item,
+ items);
if (one_chance_in(100) &&
- _make_zombie(mon, corpse_class, corpse, last_item))
+ _make_zombie(mon, corpse_class, corpse_index, fake_corpse, last_item))
{
DID_AFFECT();
}
@@ -2760,6 +2768,9 @@ void melee_attack::do_miscast()
if (!miscast_target->alive())
return;
+ if (miscast_target->atype() == ACT_PLAYER && you.banished)
+ return;
+
const bool chaos_brand =
weapon && get_weapon_brand(*weapon) == SPWPN_CHAOS;
diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc
index 0e79a1af7b..3cef8022c0 100644
--- a/crawl-ref/source/items.cc
+++ b/crawl-ref/source/items.cc
@@ -1521,7 +1521,8 @@ static void _got_item(item_def& item, int quant)
// Returns quantity of items moved into player's inventory and -1 if
// the player's inventory is full.
-int move_item_to_player( int obj, int quant_got, bool quiet )
+int move_item_to_player( int obj, int quant_got, bool quiet,
+ bool ignore_burden )
{
if (item_is_stationary(mitm[obj]))
{
@@ -1561,7 +1562,7 @@ int move_item_to_player( int obj, int quant_got, bool quiet )
bool partial_pickup = false;
- if (you.burden + imass > carrying_capacity())
+ if (!ignore_burden && (you.burden + imass > carrying_capacity()))
{
// calculate quantity we can actually pick up
int part = (carrying_capacity() - you.burden) / unit_mass;
diff --git a/crawl-ref/source/items.h b/crawl-ref/source/items.h
index a56f625c17..ecf947786b 100644
--- a/crawl-ref/source/items.h
+++ b/crawl-ref/source/items.h
@@ -40,7 +40,8 @@ void inc_mitm_item_quantity( int obj, int amount );
bool move_item_to_grid( int *const obj, const coord_def& p );
void move_item_stack_to_grid( const coord_def& from, const coord_def& to );
-int move_item_to_player( int obj, int quant_got, bool quiet = false );
+int move_item_to_player( int obj, int quant_got, bool quiet = false,
+ bool ignore_burden = false );
void mark_items_non_pickup_at(const coord_def &pos);
bool is_stackable_item( const item_def &item );
bool items_similar( const item_def &item1, const item_def &item2 );
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 4e770949ab..9cced64727 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -3360,6 +3360,11 @@ bool monsters::can_pass_through_feat(dungeon_feature_type grid) const
return mons_can_pass(this, grid);
}
+bool monsters::is_habitable_feat(dungeon_feature_type actual_grid) const
+{
+ return monster_habitable_grid(this, actual_grid);
+}
+
bool monsters::can_drown() const
{
// Mummies can fall apart in water; ghouls, vampires, and demons can
@@ -3592,6 +3597,116 @@ item_def *monsters::weapon(int which_attack)
return (weap == NON_ITEM) ? NULL : &mitm[weap];
}
+bool monsters::can_wield(const item_def& item, bool ignore_curse,
+ bool ignore_brand, bool ignore_shield,
+ bool ignore_transform) const
+{
+ // Monsters can only wield weapons or go unarmed (OBJ_UNASSIGNED
+ // means unarmed)
+ if (item.base_type != OBJ_WEAPONS && item.base_type != OBJ_UNASSIGNED)
+ return (false);
+
+ // These *are* weapons, so they can't wield another weapon or
+ // unwield themselves.
+ if (type == MONS_DANCING_WEAPON)
+ return (false);
+
+ // MF_HARD_RESET means that all items the monster is carrying will
+ // disappear when it does, so it can't accept new items or give up
+ // the ones it has.
+ if (flags & MF_HARD_RESET)
+ return (false);
+
+ // Summoned items can only be held by summoned monsters.
+ if ((item.flags & ISFLAG_SUMMONED) && !is_summoned())
+ return (false);
+
+ item_def* weap1 = NULL;
+ if (inv[MSLOT_WEAPON] != NON_ITEM)
+ weap1 = &mitm[inv[MSLOT_WEAPON]];
+
+ int avail_slots = 1;
+ item_def* weap2 = NULL;
+ if (mons_wields_two_weapons(this))
+ {
+ if (!weap1 || hands_reqd(*weap1, body_size()) != HANDS_TWO)
+ avail_slots = 2;
+
+ const int offhand = mons_offhand_weapon_index(this);
+ if (offhand != NON_ITEM)
+ weap2 = &mitm[offhand];
+ }
+
+ // If we're already wielding it, then of course we can wield it.
+ if (&item == weap1 || &item == weap2)
+ return(true);
+
+ // Barehanded needs two hands.
+ const bool two_handed = item.base_type == OBJ_UNASSIGNED
+ || hands_reqd(item, body_size()) == HANDS_TWO;
+
+ item_def* _shield = NULL;
+ if (inv[MSLOT_SHIELD] != NON_ITEM)
+ {
+ ASSERT(!(weap1 && weap2));
+
+ if (!ignore_shield && two_handed)
+ return (false);
+
+ _shield = &mitm[inv[MSLOT_SHIELD]];
+ }
+
+ if (!ignore_curse)
+ {
+ int num_cursed = 0;
+ if (weap1 && item_cursed(*weap1))
+ num_cursed++;
+ if (weap2 && item_cursed(*weap2))
+ num_cursed++;
+ if (_shield && item_cursed(*_shield))
+ num_cursed++;
+
+ if (two_handed && num_cursed > 0 || num_cursed >= avail_slots)
+ return (false);
+ }
+
+ return could_wield(item, ignore_brand, ignore_transform);
+}
+
+bool monsters::could_wield(const item_def &item, bool ignore_brand,
+ bool /* ignore_transform */) const
+{
+ ASSERT(is_valid_item(item));
+
+ // These *are* weapons, so they can't wield another weapon.
+ if (type == MONS_DANCING_WEAPON)
+ return (false);
+
+ // Monsters can't use fixed artefacts
+ if (is_fixed_artefact(item))
+ return (false);
+
+ // Wimpy monsters (e.g. kobold, goblin) can't use halberds etc.
+ if (!check_weapon_wieldable_size(item, body_size(PSIZE_BODY)))
+ return (false);
+
+ if (!ignore_brand)
+ {
+ // Demonic/undead monsters won't use holy weapons.
+ if (mons_is_unholy(this) && is_holy_item(item))
+ return (false);
+
+ // Holy monsters won't use demonic or chaotic weapons.
+ if (mons_holiness(this) == MH_HOLY
+ && (is_evil_item(item) || get_weapon_brand(item) == SPWPN_CHAOS))
+ {
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
bool monsters::can_throw_large_rocks() const
{
@@ -3855,7 +3970,7 @@ bool monsters::unequip(item_def &item, int slot, int near, bool force)
if (!force && item.cursed())
return (false);
- if (!force && mons_near(this) && player_monster_visible(this))
+ if (!force && you.can_see(this))
set_ident_flags(item, ISFLAG_KNOW_CURSE);
switch (item.base_type)
@@ -4146,9 +4261,8 @@ bool monsters::pickup_throwable_weapon(item_def &item, int near)
bool monsters::wants_weapon(const item_def &weap) const
{
- // monsters can't use fixed artefacts
- if (is_fixed_artefact( weap ))
- return (false);
+ if (!could_wield(weap))
+ return (false);
// Blademasters and master archers like their starting weapon and
// don't want another, thank you.
@@ -4158,10 +4272,6 @@ bool monsters::wants_weapon(const item_def &weap) const
return (false);
}
- // Wimpy monsters (e.g. kobold, goblin) shouldn't pick up halberds etc.
- if (!check_weapon_wieldable_size(weap, body_size(PSIZE_BODY)))
- return (false);
-
// Monsters capable of dual-wielding will always prefer two weapons
// to a single two-handed one, however strong.
if (mons_wields_two_weapons(this)
@@ -4170,22 +4280,13 @@ bool monsters::wants_weapon(const item_def &weap) const
return (false);
}
- // Nobody picks up giant clubs.
- // Starting equipment is okay, of course.
+ // Nobody picks up giant clubs. Starting equipment is okay, of course.
if (weap.sub_type == WPN_GIANT_CLUB
|| weap.sub_type == WPN_GIANT_SPIKED_CLUB)
{
return (false);
}
- // Demonic/undead monsters won't pick up holy wrath.
- if (get_weapon_brand(weap) == SPWPN_HOLY_WRATH && mons_is_unholy(this))
- return (false);
-
- // Holy monsters won't pick up demonic weapons.
- if (mons_holiness(this) == MH_HOLY && is_evil_item(weap))
- return (false);
-
return (true);
}
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index cebd0170e1..513388d10a 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -308,11 +308,21 @@ void monster_drop_ething(monsters *monster, bool mark_item_origins,
mprf(MSGCH_SOUND, grid_item_destruction_message(grd(monster->pos())));
}
-static void _place_monster_corpse(const monsters *monster, bool silent,
- int summon_type)
+int fill_out_corpse(const monsters* monster, item_def& corpse,
+ bool allow_weightless)
{
- bool force = false;
- int corpse_class = mons_species(monster->type);
+ ASSERT(monster->type != -1 && monster->type != MONS_PROGRAM_BUG);
+ corpse.clear();
+
+ int summon_type;
+ if (mons_is_summoned(monster, NULL, &summon_type)
+ || mons_enslaved_body_and_soul(monster)
+ || (monster->flags & (MF_BANISHED | MF_HARD_RESET)))
+ {
+ return (-1);
+ }
+
+ int corpse_class = mons_species(monster->type);
// If this was a corpse that was temporarily animated then turn the
// monster back into a corpse.
@@ -321,7 +331,6 @@ static void _place_monster_corpse(const monsters *monster, bool silent,
|| summon_type == SPELL_ANIMATE_SKELETON
|| summon_type == MON_SUMM_ANIMATE))
{
- force = true;
corpse_class = mons_zombie_base(monster);
}
@@ -334,28 +343,39 @@ static void _place_monster_corpse(const monsters *monster, bool silent,
corpse_class = MONS_GLOWING_SHAPESHIFTER;
// Doesn't leave a corpse.
- if (mons_weight(corpse_class) == 0 || mons_enslaved_body_and_soul(monster)
- || (!force && coinflip()))
- {
- return;
- }
+ if (mons_weight(corpse_class) == 0 && !allow_weightless)
+ return (-1);
+
+ corpse.flags = 0;
+ corpse.base_type = OBJ_CORPSES;
+ corpse.plus = corpse_class;
+ corpse.plus2 = 0; // butcher work done
+ corpse.sub_type = CORPSE_BODY;
+ corpse.special = FRESHEST_CORPSE; // rot time
+ corpse.quantity = 1;
+ corpse.orig_monnum = monster->type + 1;
+ corpse.props[MONSTER_NUMBER] = short(monster->number);
+
+ corpse.colour = mons_class_colour(corpse_class);
+ if (corpse.colour == BLACK)
+ corpse.colour = monster->colour;
+
+ return (corpse_class);
+}
+static void _place_monster_corpse(const monsters *monster, bool silent)
+{
int o = get_item_slot();
if (o == NON_ITEM)
return;
- mitm[o].flags = 0;
- mitm[o].base_type = OBJ_CORPSES;
- mitm[o].plus = corpse_class;
- mitm[o].plus2 = 0; // butcher work done
- mitm[o].sub_type = CORPSE_BODY;
- mitm[o].special = FRESHEST_CORPSE; // rot time
- mitm[o].colour = mons_class_colour(corpse_class);
- mitm[o].quantity = 1;
- mitm[o].props[MONSTER_NUMBER] = short(monster->number);
+ const int corpse_class = fill_out_corpse(monster, mitm[o]);
- if (mitm[o].colour == BLACK)
- mitm[o].colour = monster->colour;
+ // Don't place a corpse? If a zombified monster is somehow capable
+ // of leaving a corpse then always place it.
+ const bool force = mons_class_is_zombified(monster->type);
+ if (corpse_class == -1 || (!force && coinflip()))
+ return;
if (grid_destroys_items(grd(monster->pos())))
{
@@ -1553,9 +1573,9 @@ void monster_die(monsters *monster, killer_type killer,
curr_PlaceInfo.assert_validity();
_monster_die_cloud(monster, true, silent, summoned, summon_type);
+ // Have to add case for disintegration effect here? {dlb}
if (!summoned)
- // Have to add case for disintegration effect here? {dlb}
- _place_monster_corpse(monster, silent, summon_type);
+ _place_monster_corpse(monster, silent);
}
_fire_monster_death_event(monster, killer, killer_index);
diff --git a/crawl-ref/source/monstuff.h b/crawl-ref/source/monstuff.h
index 2d73b665aa..8916801a6e 100644
--- a/crawl-ref/source/monstuff.h
+++ b/crawl-ref/source/monstuff.h
@@ -89,6 +89,9 @@ bool monster_polymorph(monsters *monster, monster_type targetc,
void monster_die(monsters *monster, killer_type killer,
int killer_index, bool silent = false, bool wizard = false);
+int fill_out_corpse(const monsters* monster, item_def& corpse,
+ bool allow_weightless = false);
+
void mons_check_pool(monsters *monster, const coord_def &oldpos,
killer_type killer = KILL_NONE, int killnum = -1);
diff --git a/crawl-ref/source/overmap.cc b/crawl-ref/source/overmap.cc
index cc371f4165..5308280b93 100644
--- a/crawl-ref/source/overmap.cc
+++ b/crawl-ref/source/overmap.cc
@@ -81,6 +81,30 @@ void seen_notable_thing( dungeon_feature_type which_thing,
_seen_other_thing( which_thing, pos );
}
+bool move_notable_thing(const coord_def& orig, const coord_def& dest)
+{
+ ASSERT(in_bounds(orig) && in_bounds(dest));
+ ASSERT(orig != dest);
+ ASSERT(!is_notable_terrain(grd(dest)));
+
+ if (!is_notable_terrain(grd(orig)))
+ return (false);
+
+ level_pos pos1(level_id::current(), orig);
+ level_pos pos2(level_id::current(), dest);
+
+ shops_present[pos2] = shops_present[pos1];
+ altars_present[pos2] = altars_present[pos1];
+ portals_present[pos2] = portals_present[pos1];
+ portal_vaults_present[pos2] = portal_vaults_present[pos1];
+ portal_vault_notes[pos2] = portal_vault_notes[pos1];
+ portal_vault_colours[pos2] = portal_vault_colours[pos1];
+
+ unnotice_feature(pos1);
+
+ return (true);
+}
+
static dungeon_feature_type portal_to_feature(portal_type p)
{
switch ( p )
diff --git a/crawl-ref/source/overmap.h b/crawl-ref/source/overmap.h
index cbba556c1f..60c71beb73 100644
--- a/crawl-ref/source/overmap.h
+++ b/crawl-ref/source/overmap.h
@@ -14,6 +14,7 @@
#include <vector>
void seen_notable_thing(dungeon_feature_type which_thing, const coord_def& pos);
+bool move_notable_thing(const coord_def& orig, const coord_def& dest);
bool overmap_knows_portal(dungeon_feature_type portal);
void display_overmap();
bool unnotice_feature(const level_pos &pos);
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index 8d5ff3a482..ef3d1a5f66 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -5560,6 +5560,16 @@ bool actor::can_pass_through(const coord_def &c) const
return can_pass_through_feat(grd(c));
}
+bool actor::is_habitable(const coord_def &_pos) const
+{
+ return is_habitable_feat(grd(_pos));
+}
+
+bool player::is_habitable_feat(dungeon_feature_type actual_grid) const
+{
+ return can_pass_through_feat(actual_grid);
+}
+
bool actor::handle_trap()
{
trap_def* trap = find_trap(pos());
@@ -6084,6 +6094,64 @@ item_def *player::weapon(int /* which_attack */)
return slot_item(EQ_WEAPON);
}
+bool actor::can_wield(const item_def* item, bool ignore_curse,
+ bool ignore_brand, bool ignore_shield,
+ bool ignore_transform) const
+{
+ if (item == NULL)
+ {
+ // Unarmed combat.
+ item_def fake;
+ fake.base_type = OBJ_UNASSIGNED;
+ return can_wield(fake, ignore_curse, ignore_brand, ignore_transform);
+ }
+ else
+ return can_wield(*item, ignore_curse, ignore_brand, ignore_transform);
+}
+
+bool player::can_wield(const item_def& item, bool ignore_curse,
+ bool ignore_brand, bool ignore_shield,
+ bool ignore_transform) const
+{
+ if (equip[EQ_WEAPON] != -1 && !ignore_curse)
+ {
+ if (item_cursed(inv[equip[EQ_WEAPON]]))
+ return (false);
+ }
+
+ // Unassigned means unarmed combat.
+ const bool two_handed = item.base_type == OBJ_UNASSIGNED
+ || hands_reqd(item, body_size()) == HANDS_TWO;
+
+ if (two_handed && !ignore_shield && equip[EQ_SHIELD] != -1)
+ return (false);
+
+ return could_wield(item, ignore_brand, ignore_transform);
+}
+
+bool player::could_wield(const item_def &item, bool ignore_brand,
+ bool /* ignore_transform */) const
+{
+ if (!check_weapon_wieldable_size(item, body_size(PSIZE_BODY)))
+ return (false);
+
+ if (!ignore_brand)
+ {
+ if (is_holy_item(item)
+ && (you.is_undead || you.species == SP_DEMONSPAWN))
+ {
+ return (false);
+ }
+ }
+
+#if 0
+ if (!ignore_transform && !transform_can_equip_type( EQ_WEAPON ))
+ return (false);
+#endif
+
+ return (true);
+}
+
item_def *player::shield()
{
if (!you_tran_can_wear(EQ_SHIELD))
@@ -6185,6 +6253,16 @@ bool player::alive() const
return (true);
}
+bool player::is_summoned(int* _duration, int* summon_type) const
+{
+ if (_duration != NULL)
+ *_duration = -1;
+ if (summon_type != NULL)
+ *summon_type = 0;
+
+ return (false);
+}
+
bool player::fumbles_attack(bool verbose)
{
// Fumbling in shallow water.
diff --git a/crawl-ref/source/spells3.cc b/crawl-ref/source/spells3.cc
index a63facdc81..c03ab68b98 100644
--- a/crawl-ref/source/spells3.cc
+++ b/crawl-ref/source/spells3.cc
@@ -777,8 +777,12 @@ static void _equip_undead(const coord_def &a, int corps, int monster,
}
static bool _raise_remains(const coord_def &a, int corps, beh_type beha,
- unsigned short hitting, god_type god, bool actual)
+ unsigned short hitting, god_type god, bool actual,
+ int* mon_index)
{
+ if (mon_index != NULL)
+ *mon_index = -1;
+
const item_def& item = mitm[corps];
if (!_animatable_remains(item))
@@ -815,6 +819,8 @@ static bool _raise_remains(const coord_def &a, int corps, beh_type beha,
a, hitting,
0, god,
zombie_type, number));
+ if (mon_index != NULL)
+ *mon_index = monster;
if (monster != -1)
{
@@ -843,7 +849,7 @@ static bool _raise_remains(const coord_def &a, int corps, beh_type beha,
bool animate_remains(const coord_def &a, corpse_type class_allowed,
beh_type beha, unsigned short hitting,
god_type god, bool actual,
- bool quiet)
+ bool quiet, int* mon_index)
{
if (is_sanctuary(a))
return (false);
@@ -859,7 +865,8 @@ bool animate_remains(const coord_def &a, corpse_type class_allowed,
{
const bool was_butchering = is_being_butchered(*si);
- success = _raise_remains(a, si.link(), beha, hitting, god, actual);
+ success = _raise_remains(a, si.link(), beha, hitting, god, actual,
+ mon_index);
if (actual && success)
{
diff --git a/crawl-ref/source/spells3.h b/crawl-ref/source/spells3.h
index 776b39213b..3460a91368 100644
--- a/crawl-ref/source/spells3.h
+++ b/crawl-ref/source/spells3.h
@@ -130,7 +130,7 @@ bool cast_summon_horrible_things(int pow, god_type god = GOD_NO_GOD);
bool animate_remains(const coord_def &a, corpse_type class_allowed,
beh_type beha, unsigned short hitting,
god_type god = GOD_NO_GOD, bool actual = true,
- bool quiet = false);
+ bool quiet = false, int* mon_index = NULL);
// last updated 24may2000 {dlb}
/* ***********************************************************************
diff --git a/crawl-ref/source/terrain.cc b/crawl-ref/source/terrain.cc
index 60b8c03dee..3ef428aa15 100644
--- a/crawl-ref/source/terrain.cc
+++ b/crawl-ref/source/terrain.cc
@@ -12,6 +12,7 @@
#include <algorithm>
+#include "cloud.h"
#include "dgnevent.h"
#include "directn.h"
#include "itemprop.h"
@@ -504,7 +505,9 @@ static void _dgn_check_terrain_monsters(const coord_def &pos)
}
-// Um, what does this do? (jpeg)
+// Clear blood off of terrain that shouldn't have it. Also clear
+// of blood if a bloody wall has been dug out and replaced by a floor,
+// or if a bloody floor has been replaced by a wall.
static void _dgn_check_terrain_blood(const coord_def &pos,
dungeon_feature_type old_feat,
dungeon_feature_type new_feat)
@@ -530,6 +533,30 @@ static void _dgn_check_terrain_blood(const coord_def &pos,
}
}
+void _dgn_check_terrain_player(const coord_def pos)
+{
+ if (pos != you.pos())
+ return;
+
+ if (you.can_pass_through(pos) || !grid_is_solid(grd(pos)))
+ {
+ if (!you.airborne())
+ {
+ // If the monster can't stay submerged in the new terrain
+ // and there aren't any adjacent squares where it can
+ // stay submerged then move it.
+ if (mgrd(you.pos()) != NON_MONSTER
+ && !mons_is_submerged( &menv[ mgrd(you.pos()) ] ))
+ {
+ monster_teleport( &menv[ mgrd(you.pos()) ], true, false);
+ }
+ move_player_to_grid(pos, false, true, false);
+ }
+ }
+ else
+ you_teleport_now(true, false);
+}
+
void dungeon_terrain_changed(const coord_def &pos,
dungeon_feature_type nfeat,
bool affect_player,
@@ -558,28 +585,145 @@ void dungeon_terrain_changed(const coord_def &pos,
_dgn_check_terrain_items(pos, preserve_items);
_dgn_check_terrain_monsters(pos);
- if (affect_player && pos == you.pos())
+ if (affect_player)
+ _dgn_check_terrain_player(pos);
+
+ set_terrain_changed(pos.x, pos.y);
+}
+
+bool swap_features(const coord_def &pos1, const coord_def &pos2,
+ bool swap_everything)
+{
+ ASSERT(in_bounds(pos1) && in_bounds(pos2));
+ ASSERT(pos1 != pos2);
+
+ const dungeon_feature_type feat1 = grd(pos1);
+ const dungeon_feature_type feat2 = grd(pos2);
+
+ if (is_sanctuary(pos1) || is_sanctuary(pos2))
+ return (false);
+
+ if (is_notable_terrain(feat1) && !see_grid(pos1)
+ && is_terrain_known(pos1.x, pos1.y))
+ {
+ return (false);
+ }
+
+ if (is_notable_terrain(feat2) && !see_grid(pos2)
+ && is_terrain_known(pos2.x, pos2.y))
+ {
+ return (false);
+ }
+
+ const unsigned short col1 = env.grid_colours(pos1);
+ const unsigned short col2 = env.grid_colours(pos2);
+
+ const unsigned long prop1 = env.map(pos1).property;
+ const unsigned long prop2 = env.map(pos2).property;
+
+ trap_def* trap1 = find_trap(pos1);
+ trap_def* trap2 = find_trap(pos2);
+
+ // Find a temporary holding place for pos1 stuff to be moved to
+ // before pos2 is moved to pos1.
+ coord_def temp(-1, -1);
+ for (int x = X_BOUND_1 + 1; x < X_BOUND_2; x++)
{
- if (!grid_is_solid(grd(pos)))
+ for (int y = Y_BOUND_1 + 1; y < Y_BOUND_2; y++)
{
- if (!you.airborne())
+ coord_def pos(x, y);
+ if (pos == pos1 || pos == pos2)
+ continue;
+
+ if (!env.markers.find(pos, MAT_ANY)
+ && !is_notable_terrain(grd(pos))
+ && env.cgrid(pos) == EMPTY_CLOUD)
{
- // If the monster can't stay submerged in the new terrain
- // and there aren't any adjacent squares where it can
- // stay submerged then move it.
- if (mgrd(you.pos()) != NON_MONSTER
- && !mons_is_submerged( &menv[ mgrd(you.pos()) ] ))
- {
- monster_teleport( &menv[ mgrd(you.pos()) ], true, false);
- }
- move_player_to_grid(pos, false, true, false);
+ temp = pos;
+ break;
}
}
- else
- you_teleport_now(true, false);
+ if (in_bounds(temp))
+ break;
}
- set_terrain_changed(pos.x, pos.y);
+ if (!in_bounds(temp))
+ {
+ mpr("swap_features(): No boring squares on level?", MSGCH_ERROR);
+ return (false);
+ }
+
+ (void) move_notable_thing(pos1, temp);
+ env.markers.move(pos1, temp);
+ dungeon_events.move_listeners(pos1, temp);
+ grd(pos1) = DNGN_UNSEEN;
+ env.map(pos1).property = 0;
+
+ (void) move_notable_thing(pos2, pos1);
+ env.markers.move(pos2, pos1);
+ dungeon_events.move_listeners(pos2, pos1);
+ grd(pos2) = feat1;
+ grd(pos1) = feat2;
+ env.map(pos1).property = prop2;
+ env.map(pos2).property = prop1;
+
+ (void) move_notable_thing(temp, pos2);
+ env.markers.move(temp, pos2);
+ dungeon_events.move_listeners(temp, pos2);
+
+ env.grid_colours(pos1) = col2;
+ env.grid_colours(pos2) = col1;
+
+ if (trap1)
+ trap1->pos = pos2;
+ if (trap2)
+ trap2->pos = pos1;
+
+ if (!swap_everything)
+ {
+ _dgn_check_terrain_items(pos1, false);
+ _dgn_check_terrain_monsters(pos1);
+ _dgn_check_terrain_player(pos1);
+ set_terrain_changed(pos1.x, pos1.y);
+
+ _dgn_check_terrain_items(pos2, false);
+ _dgn_check_terrain_monsters(pos2);
+ _dgn_check_terrain_player(pos2);
+ set_terrain_changed(pos2.x, pos2.y);
+ return (true);
+ }
+
+ const int i1 = igrd(pos1);
+ const int i2 = igrd(pos2);
+
+ igrd(pos1) = i2;
+ igrd(pos2) = i1;
+
+ const int m1 = mgrd(pos1);
+ const int m2 = mgrd(pos1);
+
+ mgrd(pos1) = m2;
+ mgrd(pos2) = m1;
+
+ move_cloud(env.cgrid(pos1), temp);
+ move_cloud(env.cgrid(pos2), pos1);
+ move_cloud(env.cgrid(temp), pos2);
+
+ if (pos1 == you.pos())
+ {
+ you.position = pos2;
+ viewwindow(true, false);
+ }
+ else if (pos2 == you.pos())
+ {
+ you.position = pos1;
+ viewwindow(true, false);
+ }
+
+ set_terrain_changed(pos1.x, pos1.y);
+ set_terrain_changed(pos2.x, pos2.y);
+
+ return (false);
}
// Returns true if we manage to scramble free.
diff --git a/crawl-ref/source/terrain.h b/crawl-ref/source/terrain.h
index 4e3df832c8..c3f855a346 100644
--- a/crawl-ref/source/terrain.h
+++ b/crawl-ref/source/terrain.h
@@ -61,6 +61,9 @@ void dungeon_terrain_changed(const coord_def &pos,
bool preserve_features = false,
bool preserve_items = false);
+bool swap_features(const coord_def &pos1, const coord_def &pos2,
+ bool swap_everything = false);
+
bool is_critical_feature(dungeon_feature_type feat);
void init_feat_desc_cache();