diff options
author | zelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573> | 2008-12-11 11:12:18 +0000 |
---|---|---|
committer | zelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573> | 2008-12-11 11:12:18 +0000 |
commit | a9ac64d3b972306982b27c81632182864022f939 (patch) | |
tree | 137e07e43b0d975949d817e257645a0aa9053c23 | |
parent | dec341f5c123fcaab27d036d70dbef1456ad10fb (diff) | |
download | crawl-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
-rw-r--r-- | crawl-ref/source/externs.h | 43 | ||||
-rw-r--r-- | crawl-ref/source/fight.cc | 213 | ||||
-rw-r--r-- | crawl-ref/source/items.cc | 5 | ||||
-rw-r--r-- | crawl-ref/source/items.h | 3 | ||||
-rw-r--r-- | crawl-ref/source/mon-util.cc | 137 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.cc | 66 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.h | 3 | ||||
-rw-r--r-- | crawl-ref/source/overmap.cc | 24 | ||||
-rw-r--r-- | crawl-ref/source/overmap.h | 1 | ||||
-rw-r--r-- | crawl-ref/source/player.cc | 78 | ||||
-rw-r--r-- | crawl-ref/source/spells3.cc | 13 | ||||
-rw-r--r-- | crawl-ref/source/spells3.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/terrain.cc | 176 | ||||
-rw-r--r-- | crawl-ref/source/terrain.h | 3 |
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(); |