We had an issue like this a couple years back, but I'll be damned if I can remember the root cause. One issue we had for certain was conflicting code in onequip or onacquire. I also remember a definite conflict with our old invisible helms code - are you using invisible helms? The first thing I'd recommend is our item event debug code in all related events (un/equip, un/acquire), like so (this is what helped us narrow the problem down). We use a chat command to set the local checked for, and just leave this code in our event at all times (it's incredibly handy to be able to turn it on on a whim):
if (GetLocalInt(oPC, "DebugItemEvents")) {
object oArea = GetArea(oPC);
SendMessageToPC(oPC, COLOR_WHITE + "Item Event: Unequip" +
", Owner: " + GetName(oPC) + " (" + GetObjectString(oPC) +
"), Area: " + GetName(oArea) + " (" + GetObjectString(oArea) +
"), Item: " + GetName(oItem) + " (" + GetObjectString(oItem) +
") [x" + IntToString(GetItemStackSize(oItem)) + "]" +
", Force: " + IntToString(GetLocalInt(oPC, "ForceUnequipped")) +
", Poly: " + IntToString(GetLocalInt(oPC, "MidPolymorph")) +
COLOR_END);
}
if (GetLocalInt(oLoser, "DebugItemEvents")) {
if (!GetIsObjectValid(oPossessor))
oPossessor = GetArea(oItem);
SendMessageToPC(oLoser, COLOR_WHITE + "Item Event: Unacquire" +
", Loser: " + GetName(oLoser) + " (" + GetObjectString(oLoser) +
"), Possessor: " + GetName(oPossessor) + " (" + GetObjectString(oPossessor) +
"), Item: " + GetName(oItem) + " (" + GetObjectString(oItem) +
") [x" + IntToString(GetItemStackSize(oItem)) + "]" + COLOR_END);
}
if (GetLocalInt(oPC, "DebugItemEvents")) {
SendMessageToPC(oPC, COLOR_WHITE + "Item Event: Equip" +
", Owner: " + GetName(oPC) + " (" + GetObjectString(oPC) +
"), Area: " + GetName(oArea) + " (" + GetObjectString(oArea) +
"), Item: " + sItemName + " (" + GetObjectString(oItem) +
") [x" + IntToString(GetItemStackSize(oItem)) + "]" + COLOR_END);
}
if (GetLocalInt(oAcquire, "DebugItemEvents")) {
SendMessageToPC(oAcquire, COLOR_WHITE + "Item Event: Acquire" +
", Acquirer: " + GetName(oAcquire) + " (" + GetObjectString(oAcquire) +
"), Loser: " + GetName(oLoser) + " (" + GetObjectString(oLoser) +
"), Item: " + GetName(oItem) + " (" + GetObjectString(oItem) +
") [x" + IntToString(GetItemStackSize(oItem)) + "]" + COLOR_END);
}
If you want any of the subfunctions, let me know, though the real major thing is simply knowing where it's seizing up. Looking at the first of those above jogged my memory - see the MidPolymorph int? It's set iin our scripts whenever a pc is polymorphed (including shifting), by means of this function:
void ApplyPolymorphEffect (int nDurType, effect ePoly, object oTarget, float fDur=0.0) {
if (GetLocalInt(oTarget, "NoPoly"))
return;
SetLocalInt(oTarget, "MidPolymorph", TRUE);
ApplyEffectToObject(nDurType, ePoly, oTarget, fDur);
DeleteLocalInt(oTarget, "MidPolymorph");
}
Both onequip and onunequip return nearly immediately if that int is set, to avoid conflicts. Onequip is immediate:
void main() {
object oPC = GetPCItemLastEquippedBy();
if (GetLocalInt(oPC, "MidPolymorph") || (ScanForPolymorphEffect(oPC) > -2))
return;
<snip>
and unequip nearly immediately, awaiting only debug::
void main() {
object oPC = GetPCItemLastUnequippedBy();
object oItem = GetPCItemLastUnequipped();
if (GetLocalInt(oPC, "DebugItemEvents")) {
object oArea = GetArea(oPC);
SendMessageToPC(oPC, COLOR_WHITE + "Item Event: Unequip" +
", Owner: " + GetName(oPC) + " (" + GetObjectString(oPC) +
"), Area: " + GetName(oArea) + " (" + GetObjectString(oArea) +
"), Item: " + GetName(oItem) + " (" + GetObjectString(oItem) +
") [x" + IntToString(GetItemStackSize(oItem)) + "]" +
", Force: " + IntToString(GetLocalInt(oPC, "ForceUnequipped")) +
", Poly: " + IntToString(GetLocalInt(oPC, "MidPolymorph")) +
COLOR_END);
}
/* scripts in onequip, onunequip, onunacquire - script here MUST be before
* the "ForceUnequipped" int is deleted below */
if (GetLocalInt(oPC, "MidPolymorph") || (ScanForPolymorphEffect(oPC) > -2))
return;
<snip>
I suspect just that will alleviate the crash problem, but again, I'm going off extremely fuzzy recollections, and it's not particularly easy to troubleshoot code involving 5 different types of events (including the initial shift). One reason for the ordering is our custom ILR system, which uses a ForceUnequipped int to indicate an item in the process of being unequipped, so if the server in question is using scripted ILR of some kind, that's definitely a red flag.
With invisible helms, there was a related crash issue, caused, iirc, by some kind of infinite un/equip loop (item props of 'invisible' helms were stored on skins, tricksy when you involve shifters).
Hopefully that will provide you a few leads to hunt down. Good luck, and let me know if you have any questions.
Funky