any valid item in mitm[] which is still at (0, 0) by the time debug_item_scan() is called must be buggy. Also set the link fields of monster held items to NON_ITEM + 1 + monster_index, so that it's easy to tell which monster is holding any given item; this is used in debug_mons_scan() and monsters::pickup() to do some sanity checking of monster inventory. I've tried to thoroughly test this, but there might still be some bugs left.
Breaks savefile compatibility (or, rather, will lead to endless error messages if you use an old save file).
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@8322 c06c8d41-db1a-0410-9941-cceddc491573
S7Y7E2KDAFMTLDIXUTR673SYL5N35VXYGLRU67L42WHVYG5SEPBQC
RVYEAP2Q4PEGGBG5SHYGV3XZ2AW6EG7UZGSKATTKGMYPEANWMUVAC
PSE2BGMI3WPJ5HLD7S6KBRMESZYOLNOUSJWFRVAT2XR5JG3AEJPQC
2OY3EXIBFR22QCCKPZOA37YUI7CX7BEKRRYSDSBDKQN6VTDBD7LAC
IB4IOXRL7AMMYTUE7GKYDYI6FE5ZFDIUG7DXAZIRMNEPA7KVSSWQC
7KWDC7XFNMBLSUO2HISIROBINZBX5T67LJEEXTAORXW2YZ7VWFGAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC
EGV2HM7SD7UQSWJGLR65NQJTUBAJ7WHLM67FMH4UFP7JRSFKREPAC
PSCYVKJ7DGXAL3V5U4O6AJTRV6Q3N3SHQWAZ73VIPRTE4W64F2XAC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
SCXTTP2FDNB2A7F4XXGXSSOEKZQ7ODDGN5YBCTZXGZ22CLCEH3WQC
YF6CE2VBFK6K4V34PKBVYVQUTJRDDDCF2M5RMUGW6V6N2M4SUPLAC
T7CUIVICB74342RA32BR37T36FOX4RBSQIB5PNOHTGTGUYGDKSTQC
ASH5CK6CPBKMLGGIRJ5GKTWMS5W3OBVHTL66RTYZIPFM6KFBYA3QC
QS3ZRS3E6KL3YJHPKYEWCWJYRBJSXD5OOYF6Y25HZVECGPJRDB5QC
NRIZKLUO26UHNKB4IERXI6ECMD2IJYZACQNIUU3SH6BPLGHAJYVAC
UAJN2CFA2QHYDHW2UFAVPPHDQFCD54RKM6V2UC4AMEDJUBBLNWIQC
DTO3EUKWHZ5RJNGNCFYXSOVTIPVXPP637F2W7WFGYKJ7JK7VNKNQC
HAM54HXIO2245W6REO4RZDY2QMIH476AWWJSMYAMSYYNEBBJSHWAC
VZSAQXCHOUUAREGQBTIJVXOXVIUNACE74SHTBONNOXINDFK4FDCAC
7YUGK5Q64KG5O7GJGTUBRRLHAHBCJ5YOE23YUPT6UBKUSB67CYAQC
S34LKQDIQJLIWVIPASOJBBZ6ZCXDHP5KPS7TRBZJSCDRVNCLK6UAC
33NP4VXH6MMMH4JFK73G4ENZ2VYFKW2AWXIRITZLVIENKDOSJO2AC
K7J2IGELTIQL5KAMBV62MFVIVMEZEWCOLZ7GHFXHYPANYIF7BRZAC
ED62QWGKBPORWVKDFOQRKJXEIWZVNGR3O4KWQBDSRNPT36AYOQYAC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
SW3RLYFNRT3IJBK6LYKHKP2J2YDU7SXQWAJZX7U6S7ICYW43OMNQC
PDOFPXD2X6VI23AHKCGQ5RVDBG74CNP2E3YOHKXLOARHHBXEK3HQC
KLE4PF466VJJ56WVBMTFDS3B4KHVV5VNSKJT6DQN6BRH5NLSPNOQC
QZERCVTY5BISIKSDH6WUXGZPIBAF4KUCGSZEEGMGBCORNUXT4HXAC
DPJBWGZZPH6WNIHLLENQBL3EO2WKHEOPAF777ROBCYBNK6DY5KDQC
KH3OM4JSCYREHPD7NRLSLO2ELFGYGRC6UHGG2RGX6FP7FBU64GGQC
DCJFH6FQ5VED5NEBORB733QHJOWOWBQR4C5STNXYQKORXX23UCWAC
TK2DI6PDNMQWV3WGYEFTRNITLFJ6YXSEMKRDP3FIFGS4W4LSPSMQC
L254F6ZIU2HWGLFFGPIORTN4C3TDQ3E5JZ7Z7GQA5AEDIKL6PKDAC
Q34XE6SMDPGFALBB7DSQWDVIPPGXYAH6I3FDTB56S4RRHOBKJREAC
marshallShort(th, item.link); // unused
if (item.pos.x == -1 && item.pos.y == -1)
marshallShort(th, -1); // unused
else
marshallShort(th, item.link);
if (item.pos.x >= 0 && item.pos.y >= 0)
ASSERT(is_valid_item(item));
const monsters *other_mon = holding_monster(item);
if (other_mon != NULL)
{
if (other_mon == this)
{
if (inv[slot] == item.index())
{
mprf(MSGCH_DIAGNOSTICS, "Monster %s already holding item %s.",
name(DESC_PLAIN, true).c_str(),
item.name(DESC_PLAIN, false, true).c_str());
return (false);
}
else
{
mprf(MSGCH_DIAGNOSTICS, "Items %s thinks it's alread held by "
"monster %s.",
item.name(DESC_PLAIN, false, true).c_str(),
name(DESC_PLAIN, true).c_str());
}
}
else if (other_mon->type == -1)
{
mprf(MSGCH_DIAGNOSTICS, "Item %s, held by dead monster, being "
"picked up by monster %s.",
item.name(DESC_PLAIN, false, true).c_str(),
name(DESC_PLAIN, true).c_str());
}
else
{
mprf(MSGCH_DIAGNOSTICS, "Item %s, held by monster %s, being "
"picked up by monster %s.",
item.name(DESC_PLAIN, false, true).c_str(),
other_mon->name(DESC_PLAIN, true).c_str(),
name(DESC_PLAIN, true).c_str());
}
}
// (0,0) is where the monster items are (and they're unlinked by igrd),
// although it also contains items that are not linked in yet.
//
// Check if a monster has it:
for (int c = 0; c < MAX_MONSTERS; c++)
for (int i = 0; i < NUM_MONSTER_SLOTS; i++)
mitm[dest].pos.reset();
mitm[dest].link = NON_ITEM;
return;
}
mitm[dest].pos.reset();
mitm[dest].link = NON_ITEM;
return;
// Always return because this item might just be temporary.
mprf(MSGCH_ERROR, "Item %s claims to be held by monster %s, but "
"it isn't in the monster's inventory.",
mitm[dest].name(DESC_PLAIN, false, true).c_str(),
monster->name(DESC_PLAIN, true).c_str());
// Don't return so the debugging code can take a look at it.
}
// Unlinking a newly created item, or a a temporary one, or an item in
// the player's inventory.
else if (mitm[dest].pos.origin() || mitm[dest].pos.equals(-1, -1))
{
mitm[dest].pos.reset();
mitm[dest].link = NON_ITEM;
dungeon_events.fire_position_event(
dgn_event(DET_ITEM_PICKUP, p, 0, obj, -1), p);
// If moving an item directly from a monster to the player without the
// item having been on the grid, then it really isn't a position event.
if (in_bounds(p))
dungeon_events.fire_position_event(
dgn_event(DET_ITEM_PICKUP, p, 0, obj, -1), p);
}
static void _sanity_test_monster_inventory()
{
// Sanity forcing of monster inventory items (required?)
for (int i = 0; i < MAX_MONSTERS; i++)
{
if (menv[i].type == -1)
continue;
for (int j = 0; j < NUM_MONSTER_SLOTS; j++)
{
if (menv[i].inv[j] == NON_ITEM)
continue;
// items carried by monsters shouldn't be linked
if (mitm[menv[i].inv[j]].link != NON_ITEM)
mitm[menv[i].inv[j]].link = NON_ITEM;
}
}
// Don't check (-1,-1) player items or (0,0) monster items
if ((mitm[i].pos.x > 0 || mitm[i].pos.y > 0) && !visited[i])
const monsters* mon = holding_monster(mitm[i]);
// Don't check (-1,-1) player items or (-2, -2) monstert items
// (except to make sure that the monster is alive).
if (mitm[i].pos.origin())
{
mpr( "Unlinked temporary item:", MSGCH_ERROR );
_dump_item( name, i, mitm[i] );
}
else if (mon != NULL && !mon->type == -1)
{
mpr( "Unlinked item held by dead monster:", MSGCH_ERROR );
_dump_item( name, i, mitm[i] );
}
else if ((mitm[i].pos.x > 0 || mitm[i].pos.y > 0) && !visited[i])
}
} // if (mgrd(m->pos()) != i)
for (int j = 0; j < NUM_MONSTER_SLOTS; j++)
{
const int idx = m->inv[j];
if (idx == NON_ITEM)
continue;
item_def &item(mitm[idx]);
if (!is_valid_item(item))
{
_announce_level_prob(warned);
warned = true;
mprf(MSGCH_WARN, "Monster %s (%d, %d) holding invalid item in "
"slot %d.",
m->full_name(DESC_PLAIN, true).c_str(),
m->pos().x, m->pos().y, j);
continue;
}
}
const monsters* holder = holding_monster(item);
if (holder == NULL)
{
_announce_level_prob(warned);
warned = true;
mprf(MSGCH_WARN, "Monster %s (%d, %d) holding non-monster "
"item.",
m->full_name(DESC_PLAIN, true).c_str(),
m->pos().x, m->pos().y);
_dump_item( item.name(DESC_PLAIN, false, true).c_str(),
idx, item );
continue;
}
if (holder != m)
{
_announce_level_prob(warned);
warned = true;
mprf(MSGCH_WARN, "Monster %s (%d, %d) holding item %s, but "
"item think's it's held by monster %s "
"(%d, %d)",
m->full_name(DESC_PLAIN, true).c_str(),
m->pos().x, m->pos().y,
holder->full_name(DESC_PLAIN, true).c_str(),
holder->pos().x, holder->pos().y);
bool found = false;
for (int k = 0; k < NUM_MONSTER_SLOTS; k++)
{
if (holder->inv[k] == idx)
{
mpr("Other monster thinks it's holding the item, too.",
MSGCH_WARN);
found = true;
break;
}
}
if (!found)
mpr("Other monster isn't holding it, though.", MSGCH_WARN);
} // if (holder != m)
} // for (int j = 0; j < NUM_MONSTER_SLOTS; j++)
} // for (int i = 0; i < MAX_MONSTERS; ++i)
// Alternative weapons don't get (un)wielded unless the monster
// can wield two weapons.
if (mon_slot != MSLOT_ALT_WEAPON || mons_wields_two_weapons(mon))
mon->equip(new_item, mon_slot, 1);
unwind_var<int> save_speedinc(mon->speed_increment);
if (!mon->pickup_item(mitm[index], false, true))
{
mpr("Monster wouldn't take item.");
if (old_eq != NON_ITEM)
{
mon->inv[mon_slot] = old_eq;
if (unequipped)
mon->equip(mitm[old_eq], mon_slot, 1);
}
mitm[index].clear();
return;
}