git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@5621 c06c8d41-db1a-0410-9941-cceddc491573
SCXTTP2FDNB2A7F4XXGXSSOEKZQ7ODDGN5YBCTZXGZ22CLCEH3WQC
LHVZICXASE4UFBQQ3UEQF4DLPFGD2NNZV7FFTRUK6PVB6XLZNQQAC
ERIX6ZUTNIN7ZPU7MSY43P4DGAOZVOSCJBKP2Y2W5GRH7M5BTNQAC
WZWOQZCXUB7QX7PGQMIUCH5JM5YME25GE2GMMT2NIJP5OIZUZLBQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
ZGZVOMRXLVC42FV67RBTLBOZWFYRL4UHL54A365BR76OUIYRWQXAC
NQMXQ6OQVUSC7Y7F7IL252QW4A5JED224EECNHWAM4ZZYVNY745AC
EY6KXNVGJ5OWJQWBE3W5E4T625XZXUNJMXIEQLCZUAJASUK4KHMQC
DH3YTI6VVI727SQXO4CXSDCSBG2UN3UAWLFULBGRLBVH22ACRXIAC
5BJPWUPLJFS34FUTFJVKA4A52YMIGV6EWDXLNSDCWBJWBGVSQFGQC
7MDPXLMPCFLZS6CLZBN4FN4RDNBW4Z6Q2NNPWD2N3LOW5LQISM3AC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
ABNTYDXOCBICW3TEDGRCEHA7XJBVINEXKUXQ5ORY2XI7SKMVTGJAC
M6MBPHNAERUXYJ7KHK5FE2B44CLBY4M5MR5KY74HVDFNHVLOEQIAC
T7CUIVICB74342RA32BR37T36FOX4RBSQIB5PNOHTGTGUYGDKSTQC
AUXVWXWIFSTWFA6VZXN2FMG7FQEKRZVV6MD32VQQ7J2RKCXHAVGAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
RX6575DZOHRUXQUZH34YZGPZJF4STUPLBQDIVTINA2L6LVCKRIGQC
S6PQTZAN4UKH5SQLQZDMVTIZFHERA6WHZ7IW25VZWZXKU6ITOCPAC
GPEJOT73KMACP33IPAKFR5ROGHCOIP22VXZMQNYTGLEA2OSZUM2AC
HHGJTYLLLXXUIFOWZDV7XEXBJT6I7OUCOCW5BALLX37LCN5FEKYAC
RUJXBJYRHCLDLXQD3ZRTI7AVECGURT7E5E6PMI2GL7Q4RHYXF4IAC
FU663V7RFZNSMG5P2E7TZJZWSGXWLCCFXSVGH4Z222FIJTH4R3KAC
6QOJM3P5IONWA3LWLN7YT7KZKNSFNIGNRLL5ZIKFRQ42BDURNAEAC
DDU4A3JGN5IUIPP5IASOODKPR2WBHSDSV4FITZ6HNXNSXXQACWAQC
KBNY5FWKTEAKABFCLPC3QFKFSVZKAGXINPCIFV6WDSWFO4VCKNTAC
// last updated 24may2000 {dlb}
/* ***********************************************************************
* called from: ability - spell
* *********************************************************************** */
bool animate_a_corpse(int x, int y, corpse_type class_allowed,
beh_type beha, unsigned short hitting,
bool god_gift = false, bool actual = true,
bool silent = false);
// last updated 24may2000 {dlb}
/* ***********************************************************************
* called from: ability - it_use3 - monstuff - mstuff2 - spell
* *********************************************************************** */
int animate_dead(actor *caster, int pow, beh_type beha, unsigned short hitting,
bool god_gift = false, bool actual = true);
// updated 24may2000 {dlb}
/* ***********************************************************************
* called from: spell
* *********************************************************************** */
// Try to equip the zombie/skeleton with the objects it died with.
// This excludes items which were dropped by the player onto the corpse,
// and corpses which were picked up and moved by the player, so the player
// can't equip their undead slaves with items of their choice.
//
// The item selection logic has one problem: if a first monster without
// any items dies and leaves a corpse, and then a second monster with
// items dies on the same spot but doesn't leave a corpse, then the
// undead can be equipped with the second monster's items if the second
// monster is either of the same type as the first, or if the second
// monster wasn't killed by the player or a player's pet.
static void _equip_undead(int x, int y, int corps, int monster, int monnum)
{
// Delay this until after 0.4
#if 0
monsters* mon = &menv[monster];
monster_type type = static_cast<monster_type>(monnum);
if (mons_itemuse(monnum) < MONUSE_STARTING_EQUIPMENT)
return;
// If the player picked up and dropped the corpse then all its
// original equipment fell off.
if (mitm[corps].flags & ISFLAG_DROPPED)
return;
// A monster's corpse is last in the linked list after its items,
// so (for example) the first item after the second-to-last corpse
// is the first item belonging to the last corpse.
int objl = igrd[x][y];
int first_obj = NON_ITEM;
while (objl != NON_ITEM && objl != corps)
{
item_def item(mitm[objl]);
if (item.base_type == OBJ_CORPSES)
{
first_obj = NON_ITEM;
continue;
}
if (first_obj == NON_ITEM)
first_obj = objl;
objl = item.link;
}
ASSERT(objl == corps);
if (first_obj == NON_ITEM)
return;
// Iterate backwards over the list, since the items earlier in the
// linked list were dropped most recently and hence more likely to
// be items the monster didn't die with.
std::vector<int> item_list;
objl = first_obj;
while (objl != NON_ITEM && objl != corps)
{
item_list.push_back(objl);
objl = mitm[objl].link;
}
for (int i = item_list.size() - 1; i >= 0; i--)
{
objl = item_list[i];
item_def &item(mitm[objl]);
// Stop equipping monster if the item probably didn't originally
// belong to the monster.
if ( (origin_known(item) && (item.orig_monnum - 1) != monnum)
|| (item.flags & (ISFLAG_DROPPED | ISFLAG_THROWN))
|| item.base_type == OBJ_CORPSES)
{
return;
}
mon_inv_type mslot;
switch(item.base_type)
{
case OBJ_WEAPONS:
if (mon->inv[MSLOT_WEAPON] != NON_ITEM)
{
if (mons_wields_two_weapons(type))
mslot = MSLOT_ALT_WEAPON;
else
{
if (is_range_weapon(mitm[mon->inv[MSLOT_WEAPON]])
== is_range_weapon(item))
{
// Two different items going into the same
// slot indicate that this and further items
// weren't equipment the monster died with.
return;
}
else
// The undead are too stupid to switch between weapons.
continue;
}
}
else
mslot = MSLOT_WEAPON;
break;
case OBJ_ARMOUR:
mslot = equip_slot_to_mslot(get_armour_slot(item));
// A piece of armour which can't be worn indicates that this
// and further items weren't the equipment the monster died
// with.
if (mslot == NUM_MONSTER_SLOTS)
return;
break;
case OBJ_MISSILES:
mslot = MSLOT_MISSILE;
break;
case OBJ_GOLD:
mslot = MSLOT_GOLD;
break;
// The undead are too stupid to use these.
case OBJ_WANDS:
case OBJ_SCROLLS:
case OBJ_POTIONS:
case OBJ_MISCELLANY:
continue;
default:
continue;
} // switch
// Two different items going into the same slot indicate that
// this and further items weren't equipment the monster died
// with.
if (mon->inv[mslot] != NON_ITEM)
return;
unlink_item(objl);
mon->inv[mslot] = objl;
if (mslot != MSLOT_ALT_WEAPON || mons_wields_two_weapons(mon))
mon->equip(item, mslot, 0);
} // while
#endif
}
static bool _raise_corpse(int x, int y, int corps, beh_type beha,
unsigned short hitting, bool god_gift, bool actual)
{
const item_def& item = mitm[corps];
if (!_is_animatable_corpse(item))
return (false);
if (!actual)
return (true);
const monster_type zombie_type =
static_cast<monster_type>(item.plus);
const int number = (item.props.exists(MONSTER_NUMBER)) ?
item.props[MONSTER_NUMBER].get_short() : 0;
// Headless hydras cannot be raised, sorry.
if (zombie_type == MONS_HYDRA && number == 0)
return (false);
monster_type mon = MONS_PROGRAM_BUG;
if (item.sub_type == CORPSE_BODY)
{
mon = (mons_zombie_size(item.plus) == Z_SMALL) ?
MONS_ZOMBIE_SMALL : MONS_ZOMBIE_LARGE;
}
else
{
mon = (mons_zombie_size(item.plus) == Z_SMALL) ?
MONS_SKELETON_SMALL : MONS_SKELETON_LARGE;
}
int monster = create_monster(
mgen_data(mon, beha, 0,
coord_def(x, y), hitting,
god_gift ? MF_GOD_GIFT : 0,
zombie_type, number));
if (monster != -1)
{
const int monnum = item.orig_monnum - 1;
if (mons_is_unique(monnum))
{
menv[monster].mname = origin_monster_name(item);
// Special case for Blork the orc: shorten his name to "Blork"
// to avoid mentions of "Blork the orc the orc skeleton".
if (monnum == MONS_BLORK_THE_ORC)
menv[monster].mname = "Blork";
}
_equip_undead(x, y, corps, monster, monnum);
}
destroy_item(corps);
return (true);
}
bool animate_a_corpse(int x, int y, corpse_type class_allowed,
beh_type beha, unsigned short hitting,
bool god_gift, bool actual,
bool silent)
{
bool success = false;
int corps = igrd[x][y];
// This searches all the items on the ground for a corpse.
while (corps != NON_ITEM)
{
const item_def& item = mitm[corps];
if (_is_animatable_corpse(item)
&& (class_allowed == CORPSE_BODY
|| item.sub_type == CORPSE_SKELETON))
{
bool was_butchering = is_being_butchered(item);
success = _raise_corpse(x, y, corps, beha, hitting, god_gift,
actual);
if (actual && success)
{
if (!silent)
{
if (was_butchering)
mpr("The corpse you are butchering rises to attack!");
if (is_terrain_seen(x, y))
mpr("The dead are walking!");
}
if (was_butchering)
xom_is_stimulated(255);
}
break;
}
corps = item.link;
}
return (success);
}
int animate_dead(actor *caster, int pow, beh_type beha, unsigned short hitting,
bool god_gift, bool actual)
{
UNUSED(pow);
static env_show_grid losgrid;
const coord_def c(caster->pos());
int minx = c.x - 6;
int maxx = c.x + 7;
int miny = c.y - 6;
int maxy = c.y + 7;
int xinc = 1;
int yinc = 1;
int number_raised = 0;
int number_seen = 0;
if (coinflip())
{
minx = c.x + 6;
maxx = c.x - 7;
xinc = -1;
}
if (coinflip())
{
miny = c.y + 6;
maxy = c.y - 7;
yinc = -1;
}
if (caster != &you)
losight(losgrid, grd, c.x, c.y, true);
env_show_grid &los(caster == &you? env.no_trans_show : losgrid);
coord_def a;
for (a.x = minx; a.x != maxx; a.x += xinc)
{
for (a.y = miny; a.y != maxy; a.y += yinc)
{
if (!in_bounds(a) || !see_grid(los, c, a))
continue;
int corps = igrd(a);
if (corps != NON_ITEM)
{
// This searches all the items on the ground for a
// corpse. Only one of a stack will be raised.
while (corps != NON_ITEM)
{
if (animate_a_corpse(a.x, a.y, CORPSE_BODY, beha,
hitting, god_gift, actual, true))
{
number_raised++;
if (see_grid(env.show, you.pos(), a))
number_seen++;
break;
}
corps = mitm[corps].link;
}
}
}
}
return (number_raised);
}
// last updated 24may2000 {dlb}
/* ***********************************************************************
* called from: ability - spell
* *********************************************************************** */
int animate_a_corpse(int axps, int ayps, beh_type corps_beh,
int corps_hit, int class_allowed);
// last updated 24may2000 {dlb}
/* ***********************************************************************
* called from: ability - it_use3 - monstuff - mstuff2 - spell
* *********************************************************************** */
int animate_dead(actor *caster, int power, beh_type corps_beh,
int corps_hit, int actual);
int animate_dead( actor *caster, int power, beh_type corps_beh,
int corps_hit, int actual )
{
UNUSED( power );
static env_show_grid losgrid;
const coord_def c(caster->pos());
int minx = c.x - 6;
int maxx = c.x + 7;
int miny = c.y - 6;
int maxy = c.y + 7;
int xinc = 1;
int yinc = 1;
int number_raised = 0;
int number_seen = 0;
if (coinflip())
{
minx = c.x + 6;
maxx = c.x - 7;
xinc = -1;
}
if (coinflip())
{
miny = c.y + 6;
maxy = c.y - 7;
yinc = -1;
}
if (caster != &you)
losight(losgrid, grd, c.x, c.y, true);
env_show_grid &los(caster == &you? env.no_trans_show : losgrid);
coord_def a;
bool was_butchered = false;
for (a.x = minx; a.x != maxx; a.x += xinc)
for (a.y = miny; a.y != maxy; a.y += yinc)
{
if (!in_bounds(a) || !see_grid(los, c, a))
continue;
if (igrd(a) != NON_ITEM)
{
int objl = igrd(a);
int hrg = 0;
// This searches all the items on the ground for a corpse.
// Only one of a stack will be raised.
while (objl != NON_ITEM)
{
if (is_animatable_corpse(mitm[objl])
&& !is_being_butchered(mitm[objl]))
{
if (is_being_butchered(mitm[objl], false))
was_butchered = true;
int num = raise_corpse(objl, a.x, a.y, corps_beh,
corps_hit, actual);
number_raised += num;
if (see_grid(env.show, you.pos(), a))
number_seen += num;
break;
}
hrg = mitm[objl].link;
objl = hrg;
}
objl = 1;
}
}
if (actual == 0)
return (number_raised);
if (was_butchered)
mpr("The corpse you are butchering rises to attack!");
if (number_seen > 0)
mpr("The dead are walking!");
return (number_raised);
}
int animate_a_corpse( int axps, int ayps, beh_type corps_beh, int corps_hit,
int class_allowed )
{
int rc = 0;
int objl = igrd[axps][ayps];
// This searches all the items on the ground for a corpse
while (objl != NON_ITEM)
{
const item_def& item = mitm[objl];
if (is_animatable_corpse(item)
&& (class_allowed == CORPSE_BODY
|| item.sub_type == CORPSE_SKELETON))
{
bool was_butchering = is_being_butchered(item);
rc = raise_corpse(objl, axps, ayps, corps_beh, corps_hit, 1);
if (rc)
{
if (was_butchering)
mpr("The corpse you are butchering rises to attack!");
if (is_terrain_seen(axps, ayps))
mpr("The dead are walking!");
if (was_butchering)
xom_is_stimulated(255);
}
break;
}
objl = item.link;
}
return rc;
}
// Try to equip the zombie/skeleton with the objects it died with.
// This excludes items which were dropped by the player onto the corpse,
// and corpses which were picked up and moved by the player, so the player
// can't equip their undead slaves with items of their choice.
//
// The item selection logic has one problem: if a first monster without
// any items dies and leaves a corpse, and then a second monster with
// items dies on the same spot but doesn't leave a corpse, then the
// undead can be equipped with the second monster's items if the second
// monster is either of the same type as the first, or if the second
// monster wasn't killed by the player or a player's pet.
static void _equip_undead( int x, int y, int corps, int monster, int monnum)
{
// Delay this until after 0.4
#if 0
monsters* mon = &menv[monster];
monster_type type = static_cast<monster_type>(monnum);
if (mons_itemuse(monnum) < MONUSE_STARTING_EQUIPMENT)
return;
// If the player picked up and dropped the corpse then all its
// original equipment fell off.
if (mitm[corps].flags & ISFLAG_DROPPED)
return;
// A monster's corpse is last in the linked list after its items,
// so (for example) the first item after the second-to-last corpse
// is the first item belonging to the last corpse.
int objl = igrd[x][y];
int first_obj = NON_ITEM;
while (objl != NON_ITEM && objl != corps)
{
item_def item(mitm[objl]);
if (item.base_type == OBJ_CORPSES)
{
first_obj = NON_ITEM;
continue;
}
if (first_obj == NON_ITEM)
first_obj = objl;
objl = item.link;
}
ASSERT(objl == corps);
if (first_obj == NON_ITEM)
return;
// Iterate backwards over the list, since the items earlier in the
// linked list were dropped most recently and hence more likely to
// be items the monster didn't die with.
std::vector<int> item_list;
objl = first_obj;
while (objl != NON_ITEM && objl != corps)
{
item_list.push_back(objl);
objl = mitm[objl].link;
}
for (int i = item_list.size() - 1; i >= 0; i--)
{
objl = item_list[i];
item_def &item(mitm[objl]);
// Stop equipping monster if the item probably didn't originally
// belong to the monster.
if ( (origin_known(item) && (item.orig_monnum - 1) != monnum)
|| (item.flags & (ISFLAG_DROPPED | ISFLAG_THROWN))
|| item.base_type == OBJ_CORPSES)
{
return;
}
mon_inv_type mslot;
switch(item.base_type)
{
case OBJ_WEAPONS:
if (mon->inv[MSLOT_WEAPON] != NON_ITEM)
{
if (mons_wields_two_weapons(type))
mslot = MSLOT_ALT_WEAPON;
else
{
if (is_range_weapon(mitm[mon->inv[MSLOT_WEAPON]])
== is_range_weapon(item))
{
// Two different items going into the same
// slot indicate that this and further items
// weren't equipment the monster died with.
return;
}
else
// The undead are too stupid to switch between weapons.
continue;
}
}
else
mslot = MSLOT_WEAPON;
break;
case OBJ_ARMOUR:
mslot = equip_slot_to_mslot(get_armour_slot(item));
// A piece of armour which can't be worn indicates that this
// and further items weren't the equipment the monster died
// with.
if (mslot == NUM_MONSTER_SLOTS)
return;
break;
case OBJ_MISSILES:
mslot = MSLOT_MISSILE;
break;
case OBJ_GOLD:
mslot = MSLOT_GOLD;
break;
// The undead are too stupid to use these.
case OBJ_WANDS:
case OBJ_SCROLLS:
case OBJ_POTIONS:
case OBJ_MISCELLANY:
continue;
default:
continue;
} // switch
// Two different items going into the same slot indicate that
// this and further items weren't equipment the monster died
// with.
if (mon->inv[mslot] != NON_ITEM)
return;
unlink_item(objl);
mon->inv[mslot] = objl;
if (mslot != MSLOT_ALT_WEAPON || mons_wields_two_weapons(mon))
mon->equip(item, mslot, 0);
} // while
#endif
}
static int raise_corpse( int corps, int corx, int cory,
beh_type corps_beh, int corps_hit, int actual )
{
int returnVal = 1;
if (!mons_zombie_size(mitm[corps].plus))
returnVal = 0;
else if (actual != 0)
{
monster_type type = MONS_PROGRAM_BUG;
if (mitm[corps].sub_type == CORPSE_BODY)
{
if (mons_zombie_size(mitm[corps].plus) == Z_SMALL)
type = MONS_ZOMBIE_SMALL;
else
type = MONS_ZOMBIE_LARGE;
}
else
{
if (mons_zombie_size(mitm[corps].plus) == Z_SMALL)
type = MONS_SKELETON_SMALL;
else
type = MONS_SKELETON_LARGE;
}
const int number =
mitm[corps].props.exists(MONSTER_NUMBER)
? mitm[corps].props[MONSTER_NUMBER].get_short()
: 0;
const monster_type zombie_type =
static_cast<monster_type>(mitm[corps].plus);
// Headless hydras cannot be raised, sorry.
if (!number && zombie_type == MONS_HYDRA)
return (0);
int monster = create_monster(
mgen_data(
type, corps_beh, 0,
coord_def(corx, cory), corps_hit,
0, zombie_type, number));
if (monster != -1)
{
const int monnum = mitm[corps].orig_monnum - 1;
if (mons_is_unique(monnum))
{
menv[monster].mname = origin_monster_name(mitm[corps]);
// Special case for Blork the orc: shorten his name to "Blork"
// to avoid mentions of "Blork the orc the orc skeleton".
if (monnum == MONS_BLORK_THE_ORC)
menv[monster].mname = "Blork";
}
_equip_undead(corx, cory, corps, monster, monnum);
}
destroy_item(corps);
}
return returnVal;
} // end raise_corpse()
// see special handling in monstuff::handle_spell {dlb}
animate_dead( monster, 5 + random2(5), SAME_ATTITUDE(monster),
monster->foe, 1 );
// see special handling in monstuff::handle_spell() {dlb}
animate_dead(monster, 5 + random2(5), SAME_ATTITUDE(monster),
monster->foe);
animate_a_corpse( you.x_pos, you.y_pos, BEH_FRIENDLY,
you.pet_target, CORPSE_BODY );
animate_a_corpse(you.x_pos, you.y_pos, CORPSE_BODY, BEH_FRIENDLY,
you.pet_target, true);
animate_dead( &you, 1 + you.skills[SK_INVOCATIONS], BEH_FRIENDLY,
you.pet_target, 1 );
animate_dead(&you, 1 + you.skills[SK_INVOCATIONS], BEH_FRIENDLY,
you.pet_target, true);