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
UPA65AL4JXYLIHH4D42IWJHRTOAF2BPOVZOAKOXBLZBYIMDZDFFQC
XJNJEBODVL4WPMFYJ3MTF3KAWLURBIZPIIC7GGRKTL2FAX7TLSLAC
SZ2WVDRM5X23JFNNW6HTWNQ2DECTIXBU7EVOQRDISPZJNLJF2QBQC
XDDH7FKCSFL76UFBC2VEX4AS23R7Y75HINAOGSW3RZSBBGMVIUGAC
ZX6XMFMK46FKFKJZGLSKQM2Z6GKCBMJU2PLOWMSN2D6OXQE4VI3QC
7EIA4DGVDBJRNHEAN3DK6SNHGALSSEUWSOL57C2VPGN2IZCQ633AC
KVDUZCENP2TCYLWIGUOS4JD4PK4TIJE5GGCAXVUBAYSF5YZFADVAC
47ADGRNSYVDBHB3JFKPZT7AUVWYTR54SCFMGGCQR2LWOUHAERQ3QC
AYU5OVG2HZO46KDAPKUWAVHS5HTYFKUWIMIRMTHAXVVFEDJE7YPAC
SOCJXX6MMOXLBEWBID4QN5FW2YNYULNNN7K3IRL7RSWK5EUNAZLQC
IXLNOTBJGHKESBCTME6QAR6XVWFNCHYGMS62V62ZJEA7VLQHXO2QC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
WDQ3ST3BQ7BMW2IXQMSJVRG7GK2EG444JLKII24FB7IJ5YF5KT5QC
J7GPW2YXLT6FGSKJ24FGQ24GTRZ6A2BDYM6CIXV4R6YBEHP6BGPAC
HIPFIMUOA7DFOFV3DQ55YZJVGNU2GNDYFUCB4MRPUR5DTYDO5YMAC
RM2JXW3ATVYRYHF3NMG5ALGI64OJ7IP2F3MDUDPUT5TBKSSN4KVQC
AV3TMWHWB3XBXQCT34UPMZBSIIKVXIGWQPNEFU4CZSBS3ZOF2CUQC
TGJZXTUIAKCFZQJ54ZQEBGFBVZSJCAX6AWDRSH3TP7UJRLGUM5SAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
SCXTTP2FDNB2A7F4XXGXSSOEKZQ7ODDGN5YBCTZXGZ22CLCEH3WQC
DZD6YG52TXNI4CJ2FRGFULBH6G5MDLRVQV3X3V35AW533Z3TVBKAC
247OW5JFN34ZV3GRVHRE25AXLLISUBPOX5YRGUE64BCM2V7QNEHAC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC
ZHFUXYUHS6V47WK2NRH7OU6RX77NRKTXOZC3MND2GG7PEEWSGFTAC
GACH6PWPGGUBEE7PFEPQMOZKSR7HTQGL2WLGF2AQPJD3FCCSKZNQC
NNG27Y5ZQAZX6UD7F7M4F6KEZBEDFXPEEC3LFUSX4ESKT7K6UJQAC
2UO6ZOW7UCP5XJ2TJ26PJNQABILK2BZ4J3BAKLOKMZPJDQHW24MAC
2YL37AGHLFOMIF3GCTVQGGV2RNNWETWM6ZMUHO3QEWDVBFFSFAKQC
BNRY5YIXLFE2TDNU2JQHWWXJQVWNSEWQ52DU7XUWIT5DZWKGBDDAC
EFWEYIB2R3DPD3JWIPU6LS6SFLPMYN7J7X4GBZR7DJWKHJ3UELSAC
JGTKZP6HCXDHEJLAONL3FNLNIZ7MUBYKXZ4CRTL46YC53TW7CBEAC
HHGJTYLLLXXUIFOWZDV7XEXBJT6I7OUCOCW5BALLX37LCN5FEKYAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
542UIZKI65UDRNEMGFFDBWYD5XC7AYLTZ3JZQRR2GHYJALD3YY6QC
3V52MSSK7QX7FWLLUW63DTWCBAJEK674EFZLKP45FLZ5KZKVARHAC
C67GX7W5HBCDPQJRSHWMLM4DUJ3ELFSHH42SVICFJVCJW25T5Z3AC
QS3ZRS3E6KL3YJHPKYEWCWJYRBJSXD5OOYF6Y25HZVECGPJRDB5QC
L254F6ZIU2HWGLFFGPIORTN4C3TDQ3E5JZ7Z7GQA5AEDIKL6PKDAC
NRIZKLUO26UHNKB4IERXI6ECMD2IJYZACQNIUU3SH6BPLGHAJYVAC
OWERGKLVPNPGIIS23FZ7ZDOBWUIXCKYAFG3URXU75JAUDX3N5ENAC
47RJZCYIM3B7IXJT7FFT6NBZZREY6REK5DZWKZ5E7G66BXEXFN6QC
C6QWJ7O4HS2IGJZTDQLGFPXPD5OBFS4U364IRZFV7V2LXVPP4DXQC
QKGDOYIYKE6B36ION5O2DRW65DWWPZMYNWJVH7LJJ7FPGGM2MYAQC
JYEEOUYQ7ZPKOGWUV7VCORBVSOLF2UCBFBH3TR75RGOSS6PNKYUAC
ASLW3Z5PAVZSWJEMMMVZT226P44EKSAD47QS72JIFJESAI3RPN3AC
RBAGQ2PB7V5YAM5KSHSZR2E3MLKDSRVM5XYGI2TIXP5QMVBOQHDQC
TV3ZC6WOZKSQQJQN26JIVKCHK6UK7WMDBYZDUYRWEAZ4JB4YVNAAC
3DQXSE4YGFBBDUWK4YEOFWW4UPWILWELFSLP37SL6BERGAZJC5YAC
5V47S4NNTHWTSAHV3YLO2VGH7JTUIYJ3GBPDN5ZM4UQALT2ZEXDQC
}
}
}
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);
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))
if (!grid_is_solid(grd(pos)))
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++)
{
for (int y = Y_BOUND_1 + 1; y < Y_BOUND_2; y++)
// 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;
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);
}
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 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);
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 (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);
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;
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);
// 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);
}
// Wimpy monsters (e.g. kobold, goblin) shouldn't pick up halberds etc.
if (!check_weapon_wieldable_size(weap, body_size(PSIZE_BODY)))
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);
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);
}
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();
// 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 (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);
}
// Is player aware of it happening?
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)
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;
if (si->plus != corpse_class)
break;
// It should have just been dropped.
if (corpse_freshness(*si) != FRESHEST_CORPSE)
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)
{
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);
// Good gods won't let their gifts/followers be raised as the undead.
if (is_good_god(mon->god))
// 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);
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.
corpse_item.link = mitm[last_item].link;
// 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;
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);
}
// 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));
return (false);
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);
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;